Agentic Traffic ControlATC

Connect your agents

Agentic Traffic Control (ATC) gathers events from three sources into one stream per project:

SourceHow it reportsWhat it sends
Claude Code hooksPOST /v1/hooks/claude, automaticallysession start and end, prompts, tool failures, permission waits, subagents, stops
The skillthe agent runs atc-emit, which calls POST /v1/eventsdecisions, questions, blockers, course changes, ticket start and finish, merge requests
GitHuba signed webhook to POST /v1/github/webhook/{project}pull requests opened, merged or closed; CI runs

Every event follows the versioned atc.event/v1 schema.

1. Create a project and a token

In the app, open Settings, create a project (for example gambit), then Create token. The token looks like atc_live_… and is shown once, so copy it now. Only a hash is stored. Each token can only write to its own project, and you can revoke it at any time.

Use one token per place agents run (your laptop, the CI runner, the cloud environment), so you can revoke one without touching the others.

2. Install the integration in your repository

From the repository root:

curl -fsSL https://agentictrafficcontrol.com/install/install.sh | sh

This writes two things, both inside .claude/:

To install by hand instead, download hooks.json, SKILL.md, atc-emit and atc-session-start to the same places.

Commit .claude/skills/ and .claude/settings.json so every agent on the repository picks them up. Never commit the token.

3. Give agents the token

The hooks and atc-emit read these environment variables:

VariableRequiredMeaning
ATC_TOKENyesThe project's ingest token.
ATC_TICKETnoThe ticket this session works on (e.g. 412). Attached to every hook event.
ATC_RUNTIMEnoclaude-code (the default), claude-code-cloud, ci…
ATC_AGENT_NAMEnoA friendly name for the board, such as worker-3.
ATC_URLnoDefaults to https://agentictrafficcontrol.com.

On your machine, set them in your shell, or in the git-ignored .claude/settings.local.json:

{ "env": { "ATC_TOKEN": "atc_live_…", "ATC_AGENT_NAME": "laptop" } }

An orchestrator that starts workers can set ATC_TICKET and ATC_AGENT_NAME for each one.

In Claude Code on the web (cloud sessions), add ATC_TOKEN (and ATC_RUNTIME=claude-code-cloud) as environment variables in the cloud environment's settings. If the environment restricts network access, add agentictrafficcontrol.com to its allowed domains.

4. Check it

sh .claude/skills/agentic-traffic-control/scripts/atc-emit check
# {"ok":true,"project":"gambit","schema":"atc.event/v1"}

Then start a Claude Code session in the repository. It appears on the Bridge within a second. Settings → Send test event checks the path from the app's side as well.

5. Connect GitHub (optional)

In the repository on GitHub, open Settings → Webhooks → Add webhook:

Deliveries are checked against the secret before anything is read. Pull requests become merge.requested, merge.completed and merge.rejected, and are matched to an agent by branch. A PR body's Closes #N sets the ticket, and a claude.ai/code/session_… link becomes the agent's session link. Workflow runs become ci.result.

How the hooks behave

Sending events directly

Anything that can make an HTTPS request can report. POST /v1/events takes one event or an array of up to 100:

curl -sS https://agentictrafficcontrol.com/v1/events \
  -H "Authorization: Bearer $ATC_TOKEN" -H 'Content-Type: application/json' \
  -d '{
    "schema": "atc.event/v1",
    "id": "01JC8ZR0000000000000000001",
    "type": "blocker.hit",
    "agent": { "id": "worker-3" },
    "ticket": { "id": "412" },
    "payload": { "cause": "flaky_test", "description": "lobby::reconnect fails 1 run in 4" }
  }'
# {"accepted":1,"duplicates":0,"rejected":[]}

Event types (v1)

TypePayload (required in bold)
session.startedcwd, source, title
session.endedreason
agent.heartbeatdoing, tokens_used
ticket.startedplan
ticket.finishedoutcome, duration_s, tokens_used
ticket.failedreason, duration_s, tokens_used
decision.madesummary, rationale, alternatives[], reversible
question.raisedquestion, urgency (low normal high critical), options[], recommendation, meanwhile
question.answeredanswer, question_id, by
blocker.hitcause, description, needs
blocker.clearedblocker_id, resolution
course.changedold_plan, new_plan, reason
ci.resultstatus (pass fail cancelled running), workflow, failing_tests[], duration_s, url
merge.requesteddiff_summary, title, files_touched[], additions, deletions, risk (low medium high), base, url
merge.completedreviewer, url
merge.rejectedreason, reviewer, url
structure.alerttarget, signal (size growth fan_in fan_out build_time churn), message, value, threshold
claude.hookmade by the server from hooks
human.actionmade by the server when you act in the app

Blocker causes: missing_context, ambiguous_ticket, flaky_test, permission, environment, dependency, build_failure, waiting_on_agent, waiting_on_human, possible_loop, other.

Answers back to agents

When you answer a question, approve a merge or send one back in the app, a human.action event is recorded against that agent. Agents read them with:

curl -sS -G https://agentictrafficcontrol.com/v1/inbox -H "Authorization: Bearer $ATC_TOKEN" \
  --data-urlencode agent=$ATC_SESSION_ID --data-urlencode after=0

(atc-emit inbox does the same.) Pass the returned next value as after to fetch only new ones.

API reference

RouteAuthPurpose
GET /v1/pingtokenCheck a token; returns the project.
POST /v1/eventstokenOne event or an array of up to 100.
POST /v1/hooks/claudetokenA raw Claude Code hook payload. Always an empty 200.
GET /v1/inbox?agent=&after=tokenhuman.action events for an agent.
POST /v1/github/webhook/{project}HMAC signatureGitHub deliveries.
GET /schema/v1.jsonnoneThe JSON Schema.

Limits: a 256 KB request body, 100 events per batch, and a per-address rate limit of 50 requests a second (bursts of 500).