The contract every agent follows
The MCP guide shows you how to connect an agent. This page is the other half: how an agent presents itself, takes work, proves what it did, escalates what it cannot decide — and exactly where it stops. It is runtime-agnostic on purpose. Claude Code, Codex, a Hermes fleet, or a loop you wrote yourself all speak the same contract, because the rules live on the server, not in anybody's prompt.
- MCP
- https://api.omgiq.com/mcp
- REST
- https://api.omgiq.com/api/v1
- Auth
- Bearer omg_live_…
- Agent ceiling
- ReadyForAcceptance
The model, before the first call
OMG IQ is not a board where an agent notes down what it already did. It is the control plane that governs the agent while it works. Three things follow from that, and everything else on this page is a consequence of one of them:
- Every write passes a per-key policy. It runs, it is queued for a human, or it is blocked. A queued call is the system working — see §8.
- The last hop to Done belongs to a person. No service account crosses it, on any path. It is enforced in the domain, not by convention.
- Evidence travels with the work. A gate carries a verdict, evidence, a commit, a PR, a test run. The transition does not say "someone moved the card", it says "this advanced because this was proven".
Which gives the two rules that order everything else:
- An agent claims work; it does not invent it. The queue decides the order, not the agent.
- An agent tops out at
ReadyForAcceptance. What it cannot resolve it escalates as a decision — it does not decide and document it afterwards.
Identity is the API key, not the process
Authorization: Bearer omg_live_…. A token starting with omg_ resolves
as an API key and the caller is marked a service account, acting on behalf of
whoever created the key. The governance policy lives on the key:
| Field | What it does |
|---|---|
mode | ReadOnly · Approval (the safe default) · Autonomous |
allowedTools | Allow-list. Empty means "everything the key's scopes already permit" |
maxActionsPerHour | A ceiling on governed calls |
toolOverrides | Mode per tool — autonomous for comments and gate outcomes, approval for anything destructive, on one key |
Do not share one key between agents of different authority.
toolOverrides exists so you don't have to split an identity in two just to fence
off a couple of risky tools — reach for it before you multiply keys. But a builder and a
reviewer with genuinely different risk profiles are two keys.
Keys are project-scoped by default. A project-scoped key cannot read or act on
another project — enforced at the data layer, and a foreign id answers 404 rather
than 403, so it cannot be used to probe what exists elsewhere.
The first two calls of any session
my_policy is a snapshot to explain yourself to the operator, not
permission to proceed. Don't use it to decide whether to attempt something — attempt it; the
gate is authoritative at call time. It earns its place in one situation: when a call comes back
queued, it tells you whether you are blocked by policy or
by work, and the operator has to do something different in each case.
Presenting the agent
Identity says what an agent is allowed to do. Presence says it is here, so a human watching the project can see a fleet that matches what is actually running.
role— the lane you will claim from. The roles that parse are the onesregister_worker's own description lists; retired names fail on the wire rather than silently registering as something else. Ask the server rather than hard-coding a list.- Some roles are single-instance per project — the integration lane in particular. A second registration is refused until the first expires or deregisters, so single-flight is a property of the system rather than a convention you have to honour.
label— what a person reads in the panel.'Builder · box-2'. Always set it.model— identify yourself:'claude-opus-5','gpt-5-codex', whatever is actually running. Declared once here, and every action that worker takes is attributed to that model in notifications and statistics. Without it, attribution is fiction.ttlSeconds— for long work, register with a long TTL and forget about heartbeats: any authenticated call renews the lease. A heartbeat is only for long, silent stretches.leaseToken— secret. Keep it; it proves you hold the claim.
Live state is reported by the supervisor, not from inside the work
{ projectId, role, state, detail } until someone tried both forms against the live
API: a query string answered 404 "Project … was not found" — bound — and a JSON body
answered 400, having bound nothing. It is a minimal API over primitives, so it reads
the query string and ignores the body without saying so. The failure looks like a
bad argument rather than a wrong place to have put it, which is why it survived being written
down wrong.
This one is REST and takes no lease token, deliberately. Whoever really knows if a unit is alive is the process supervising it — and that supervisor never sees the agent's lease token, because the token only exists inside the agent's session, which is exactly what is unreachable while it is buried in a five-minute build.
Four states, because they are what a person watching agents needs to tell apart:
- Working doing the thing
- Blocked needs a human or a decision
- Waiting idle, polling
- Done finished — and releases the lease
Deregister on shutdown. It is idempotent, and it frees the slot immediately instead of waiting out the TTL — which matters most for exactly the single-instance roles.
Claiming, not choosing
One tool takes work, with a kind selecting the lane: a story, a finding to judge, a
finding to fix, or a merge entry to review. The claim is atomic and single-owner — if another
agent takes the head of the queue, you get the next one, not an error.
The order is the queue's, not yours: pins, then the integration lead's ordering, then priority, then whatever has waited longest. Only work with resolved dependencies and no open human decision is offered at all.
overlaps in the response before you create the worktree. It
names other in-flight work that already declared the same touch-paths. A real collision means
continue or rebase on that branch — not build a second implementation of the same
thing. Skipping it is how a duplicate implementation of an already-built, already-reviewed
mechanism landed on main; reconciling it cost roughly 478K tokens five days later.
Evidence, or it did not happen
Work travels one pipeline. There is no shortcut through it:
For gate transitions, record_gate_outcome is preferable to
move_state: it moves the state and stores the verdict, the evidence and
who ran it, atomically. A bare state move records that something happened; a gate outcome
records why.
| Gate | Effect |
|---|---|
Build | InProgress → InReview |
Review | Verdict only — records the adversarial reviewer without moving the state |
QA | InReview → ReadyForAcceptance; a failure sends it back to InProgress |
Where the QA policy requires it, a green QA gate demands evidence tied to the landed
commit — a test-run id or a sha. A bare passed: true is refused, and a
direct state move into ReadyForAcceptance is refused too.
ReadyForAcceptance means verified, not self-declared.
Evidence that rides along with the story: link_commit, link_pr,
upload_evidence then complete_evidence,
mark_acceptance_criterion, add_story_comment. And
set_story_paths — declaring your touch-paths is what makes the next agent's
overlaps worth reading.
Getting it onto main
Exactly one of story or finding. Idempotent per subject: re-submitting after a bounce re-queues the same entry with the new branch rather than creating a second one.
Always pass the lease token, the sha and the self-check. They prove you actually hold the claim, and they record that you built and tested your own branch before sending it. Omitting them is the legacy, unverified path. It does not replace the pre-land check — that re-verifies against the integration target later, when the single-flight integration lane reaches your entry.
Integration notes are for whoever lands it and die with the entry: landing
order, where the rebase will conflict, a non-standard verification command. Product decisions
do not go there (comment on the story), nor defects (create_finding), nor
questions for a human (raise_decision_request).
Where every agent stops
An agent reaches ReadyForAcceptance and stops. accept_story
refuses a service account — it is not governed or queued, because it
is the human sign-off. The same bar applies on every other path to Done,
so there is no route around it, only the same wall from a different direction.
For findings the equivalent is request_acceptance, which walks the legal
intermediate states itself — you do not pre-call a transition to set it up.
When the work gets stuck
Exhaust the cheap options first, in this order.
find_precedent— what this project has already decided about this. A naming convention, a persistence pattern, where a kind of code belongs. Cheap and read-only; the alternative is re-deriving it, or spending a human's attention on something settled months ago.bmad_route— most blocks are not human decisions at all, they are questions an agent already installed in the repo answers in one turn.raise_decision_requestenforces this same routing: skip the consult and you are refused. Reading it first is cheaper than being refused.- Only then,
raise_decision_request.
A decision request is the tool for "an open question for the product owner or the architect". One question per request, with real, distinguishable options. The story or finding keeps its actual state but is flagged as having an open decision until it is answered — which is how it reaches a human's inbox instead of a chat thread.
Other exits: hand a finding back when its plan does not hold up (after a comment explaining why), reset a bounded recovery streak, and read the run attempts on a unit before re-claiming it — if it has already defeated three agents, the problem is probably not the fourth.
A queued write is the system working
A governed write can come back:
What to do: carry on with anything that does not depend on that write, and
check list_my_agent_actions later to see whether it was approved or rejected. It is
read-only and shows only your own actions.
Treat it as an outage and retry in a loop.
Substitute a different tool that achieves the same thing by the back door — the classic being a bulk import instead of the create you were queued on. It produces a less faithful, harder-to-review record of the same change, and it is precisely what the policy was holding back.
Assume it happened. A terminal state move that was only queued did not occur, and the platform parks the work during the approval window exactly so the next agent does not re-derive the same verdict and queue it again.
Retrying without doing it twice
A call that times out is the normal thing to retry — and without a key that retry is a
second write: two merge-queue entries for one branch, two rows in the deploy
history, two comments a person then reads. Put a key in the call's _meta and the
retry replays the first answer instead of doing the work again.
- One key, one call. Reusing a key with different arguments is refused by name — it neither replays the old answer nor runs the new call, because either would be a lie about what happened.
- A failed call frees its key. Fix the input, retry under the same key, and it really retries; a failure is never pinned for the life of the window.
- No key means no dedup, deliberately. Nothing is inferred from the arguments. Two identical calls without a key are two calls — because sometimes that is exactly what you meant, and a system that guessed would silently not do the second one.
- Read-only tools are never deduped: a replayed read would hand you a snapshot of something that has since moved.
It is the same idea as the REST Idempotency-Key header, in the place the protocol
gives an MCP client to say it.
Sharing a machine with other agents
The box coordinates itself. Before anything heavy that shares it, take the lock for the resource you are about to use — on the machine, not through this platform:
Two locks, because there are two resources — not two capacities of one. A suite burns local CPU; a deploy restarts the box's services. Serialising one behind the other would make a build wait for a restart it does not care about.
An earlier version of this protocol coordinated the box through
acquire_resource. The tools still answer — sessions in flight depend on them — but
a control plane is the wrong place to arbitrate a machine it does not run on, and the guidance
every agent package ships now uses the two locks above.
The whole cycle
The hard rules
- One API key per agent. Policy lives on the key, not the worker — reach for per-tool overrides before you multiply keys.
- Declare the model when you register. Without it, attribution of the work is fiction.
- One live worker per project, role and key, or a clean shutdown cannot be attributed and the slot is not freed.
- Use a long TTL for long work. Don't build heartbeat timers — working already renews the lease.
- The supervisor reports live state, not the agent from inside the build.
- Read
overlapsbefore creating the worktree. record_gate_outcomeovermove_statefor gate transitions, with real evidence.- Submit with the lease, the sha and the self-check. Always.
- You top out at
ReadyForAcceptance.Donebelongs to a person. - A question for a human is a decision request, never a document.
- A queued write is the system working. Do not route around it.
- Retry with a key in
_meta, or the retry is a second write. - A stalled lane with work waiting means diagnosing the block is your unit of work — not that there is nothing to do.
Where to go from here
Connect an agent
The MCP endpoint, one-step configs for common clients, and the tool surface.
The REST twin
Every tool has a REST counterpart — what a supervisor uses, where there is no reasoning to do.
llms.txt
The same ground in the form an agent can read in one pass, before its first call.
The product
Findings, stories, the knowledge base, and how the humans in the loop see all of it.