Skip to content

Conventions

These hold across every endpoint. Learning them once is cheaper than learning them one resource at a time.

JSON in and out (application/json), except file uploads (multipart/form-data) and the plain-text documentation endpoints.

  • Field names are snake_case everywhere, including nested configuration objects.
  • Identifiers are UUID strings.
  • Timestamps are RFC 3339 with an explicit offset, normalised to Z on output.
  • Money is a decimal string ("0.014823"), never a float. A float total does not reconcile — parse it into a decimal type or leave it as a string, but do not put it through float.
  • Token counts and durations are integers. A duration’s field name ends in _ms or _s and says which.

Enumerations are closed. Every vocabulary in the reference is the exact set of labels the database enum carries; a value outside it cannot be stored and will not appear on the wire.

Every list endpoint accepts:

Parameter
limitDefault 50, max 200.
cursorThe primary pagination mechanism.
offsetOnly when cursor is absent, capped at 10 000. For page-number UIs.
sortfield or -field, allowlisted per resource.
qFree-text search, where the resource supports it.

And answers:

{"items": [], "next_cursor": "…", "total": 1234}

Two things worth knowing:

total is null when counting is not cheap, and null means “not computed”. It never means zero. A UI that renders null as 0 will tell somebody their workspace is empty.

An unknown filter is a 422 naming it, not a silently ignored parameter. A typo’d query string fails loudly rather than quietly returning everything.

Loop on next_cursor until it is absent:

Terminal window
curl "https://your-deployment.example.com/runs?status=failed&limit=100" \
-H "X-Api-Key: $COBRE_KEY"

Implemented on POST /runs. Send the header — Idempotency-Key: <≤200 chars> is the canonical spelling — and the key is bound to a hash of the request body.

SituationResult
First useThe run is created; the key, the body hash and the response are recorded.
Replay, same bodyThe original response verbatim, with Idempotent-Replay: true.
Replay, same body, first still running409 idempotency_in_flight, Retry-After: 1.
Replay, different body422 idempotency_key_reuse — the key is bound to its body.
After 48 hThe record expires and the key is free again.

Two things before you build retries on this:

  • The hash is taken over the validated document with the key removed. Re-ordering the JSON object, or switching between the header and the body field, is the same request; changing any field is a different one.
  • run.idempotency_key is permanently unique per workspace. A repeat after the 48-hour record has expired still returns the original run rather than creating a second one — it just arrives without Idempotent-Replay, because as far as the record is concerned it is a first use.

The body field idempotency_key in RunSubmit is the older spelling and works identically, hash binding included. Sending both is fine when they agree and is 422 idempotency_key_mismatch when they do not: a client that believes it sent one key is never silently deduplicated against another.

Other POST endpoints accept the header without acting on it. Only /runs is wired.

PATCH implements RFC 7396 merge-patch:

  • a present field is set;
  • null deletes;
  • an absent field is untouched.

application/json on a PATCH is treated identically to application/merge-patch+json.

PUT is a full replacement and exists only where a full replacement is meaningful.

On a versioned resource — agent, toolkit, skill, review playbook — both verbs create a new revision and never mutate an existing one. See Agents.

Mutable single-object resources return an ETag and accept If-Match.

A mismatch is 409 stale_write with the current representation in the body, so a client can merge rather than guess. Read it, reconcile, resubmit with the new ETag.

The path carries no version segment, because breaking changes are not made — only additive ones. A field is retired with a Deprecation header and a sunset date at least 180 days out. Nothing disappears without that warning.

  • Errors — the envelope every one of those 4xxs arrives in.
  • Streaming a run — the one place the rules on this page do not apply.