Skip to main content
The HTTP API is the direct integration surface for applications that manage their own credentials, persistence, and review workflow. Its origin is https://api.antigen.sh. Authenticate every request with an organization API key in x-api-key. Keys begin with atg_sk_. Send the key only in that header: never in a URL, a browser history, a source file, or a log. Use content-type: application/json for Run creation and guidance. A local caller deadline bounds only that caller. It does not create, stop, resume, or cancel a Run.

Conventions

Every response carries contractVersion, currently "v1". Successful reads return the resource directly; errors return an error object described under Errors. Each endpoint requires a scope on the credential. See Authentication for how credentials are issued and how authorization errors are resolved. Two endpoints accept query parameters: GET /v1/runs takes cursor, limit, and status, and GET /v1/runs/{runId}/events takes after. Every other endpoint rejects a query string with 400 and INVALID_REQUEST, and so does an unrecognized parameter on those two. The message does not name the offending parameter. A filter that is silently ignored is worse than a rejected request, because a dropped status returns Runs the caller did not ask for while appearing to have succeeded.

Discovery and creation

List approved targets

GET /v1/targets Lists targets approved for the caller’s organization.
Select a target from this response for POST /v1/runs. If creation returns TARGET_NOT_APPROVED, re-read this endpoint rather than editing the rejected target.

Create a Run

POST /v1/runs Creates one authorized Run for a coherent assessment objective.
Persist id immediately with the assessment record. If the response is ambiguous, read that saved ID with GET /v1/runs/{runId} before considering another creation request. A second POST /v1/runs is a new assessment record, not a retry.

List and read Runs

List Runs

GET /v1/runs Reads an organization-visible page of Runs.
nextCursor is present only when another page exists. Pass it back unchanged. Do not derive authorization, identity, or ordering from a cursor or a Run ID.

Read a Run

GET /v1/runs/{runId} Reads one known Run by its saved opaque ID.
The response fields depend on how far the Run has progressed. startedAt appears once it is running, stoppedAt when it is stopped, and completedAt on a terminal status. A failed Run also carries failure with a code of EXECUTION_UNAVAILABLE or RUN_EXECUTION_FAILED and a message.
An ID alone grants nothing. A Run belonging to another organization answers RUN_NOT_FOUND, and a credential without runs:read answers FORBIDDEN. Read this endpoint after any ambiguous create or lifecycle request, then resume observation with GET /v1/runs/{runId}/events after your last handled checkpoint.

Events and findings

Stream Run events

GET /v1/runs/{runId}/events Opens an ordered server-sent event stream for a Run. The response uses content-type: text/event-stream and stays open until the Run reaches a terminal state or the caller disconnects. Resume with either a numeric after query parameter or an opaque Last-Event-ID header. They are alternatives, and sending both is rejected with 400 and INVALID_REQUEST.
Each frame carries the opaque event ID, the event type, and a JSON envelope:
Persist sequence only after handling that event, and resume with after=<sequence>. When you store the opaque id instead, send it as Last-Event-ID and omit after; the two cursors are not interchangeable. An invalid cursor is rejected with INVALID_REQUEST.

Read Run findings

GET /v1/runs/{runId}/findings Reads durable findings and remediation guidance for one Run.
severity is one of critical, high, medium, low, or info. proof, endpoint, and cwe appear only when available. An empty findings array is a valid outcome for a completed Run, not an integration failure.

Guidance and lifecycle

Send guidance

POST /v1/runs/{runId}/messages Delivers scoped guidance to an existing Run. The JSON body requires a content string; keep the guidance within the Run’s approved target and Guardrails.
For an ambiguous response, read the Run before sending another message. Work outside the approved scope needs its own authorized assessment.

Lifecycle actions

stop, resume, and cancel all return the Run in its requested new state. stop and resume parse a JSON body and reject a malformed one with INVALID_REQUEST, so send {} and content-type: application/json. cancel reads no body at all. DELETE /v1/runs/{runId} is an exact alias for cancel, for clients that prefer the verb.
INVALID_RUN_STATE means the action does not apply to the Run’s observed status, usually because another transition already occurred. Read the Run and choose a valid action rather than repeating the request. Resuming does not re-evaluate target approval, so confirm the scope still applies first. A local connection failure does not prove the action was rejected.

Errors

Error responses carry contractVersion and an error object with a stable code, a message, and an optional requestId.
The status code carries information the code does not, and the reverse is also true, so read both. The line between the two authorization codes is whether the credential is allowed to do this at all, which is FORBIDDEN, or whether the request itself is written incorrectly, which is INVALID_REQUEST. RUN_NOT_FOUND deliberately covers more than a missing Run. It is also the response when the Run exists but belongs to another organization. Those two cases are not distinguishable from outside, by design: a caller holding a Run ID cannot learn whether that ID is real. Do not use this endpoint to test whether an ID exists, and do not report a cross-organization Run to a user as deleted. A mistyped path is a separate code, NOT_FOUND, so a 404 about routing is never confused with a 404 about a Run. Request bodies are validated strictly. A field the endpoint does not define is rejected with INVALID_REQUEST rather than ignored, so a misspelled key fails loudly instead of silently dropping the value you meant to send. This set may grow. Handle a code you do not recognize by its HTTP status rather than by an exhaustive match, so that a new code does not turn into an unhandled failure in your client. Retain requestId with the saved Run ID whenever one is present. It lets support identify the failing request without exposing credentials.

Retrying a failed request

Retry on 408, 429, and any 5xx. Do not retry 400, 401, 403, 404, or 409: the request is wrong or no longer applicable, and repeating it produces the same response. 503 with EXECUTION_UNAVAILABLE means execution capacity is momentarily exhausted: back off and retry. The TypeScript SDK reconnects event streams with exponential backoff from a 250ms base, capped at 5s, scaled by random jitter between 0.5 and 1.5, for at most 5 consecutive failures. Those values are a reasonable starting point for a hand-written client. POST /v1/runs accepts no idempotency key. Retrying a create that failed with 503 can produce two Runs, both of which will execute and both of which are billable. When a create is ambiguous, list Runs and look for one matching your targets before sending it again. After an ambiguous create, lifecycle, or observation request, do not infer the remote result from a local failure. Preserve the Run ID and your event checkpoint, read the Run, resume events after that checkpoint, and read findings for durable remediation work. Related concepts: Get started, TypeScript SDK, Guides, and Reliability and security.