Skip to content

Push Route picks and verdicts

POST
/route/history
curl --request POST \
--url https://api.cedr.dev/v1/route/history \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '{ "org": "northwind", "records": [ { "type": "pick", "id": "p-1a2b3c4d5e6f", "at": "2026-10-29T07:42:00Z", "route": "review.code", "rules": "8ff065e", "target": "codex/gpt-6.1-sol", "effort": "high", "reason": "primary", "state": "ready", "size": "medium" }, { "type": "verdict", "id": "p-1a2b3c4d5e6f", "at": "2026-10-29T08:10:00Z", "verdict": "accepted" }, { "type": "council", "id": "c-1a2b3c4d5e6f", "at": "2026-10-29T08:20:00Z", "route": "review.council", "run": "r-1a2b3c4d5e6f", "round": 1, "rounds": 1, "gate": "block", "next": "revise", "quorum": 2, "block": "P1", "reviewers": [ "codex/gpt-6.1-sol", "claude/claude-opus-5-5" ], "groups": { "P0": 0, "P1": 1, "P2": 0, "P3": 2 }, "findings": { "codex/gpt-6.1-sol": 2, "claude/claude-opus-5-5": 1 } } ] }'

What cedr route sync sends: up to 500 history records from one machine, in order. Picks and council results are stored once per ID, so a retried batch is safe; a verdict lands only on your own pick and only when newer than the one it has. Records older than the organization’s Route history (30 days on Free, 1 year on Pro and Team, 3 years on Business) are accepted and dropped. Each record is checked on its own: skipped counts the ones refused.

Media typeapplication/json
object
org
required

The organization ID (org- and 12 hex digits)

string
records
required

History lines from cedr route sync, each a pick, a verdict or a council result (cedr’s Route contract). A record of any other type is skipped.

Array
<= 500 items
One of: discriminator: type
object
type
required
string
Allowed value: pick
id
required
string
/^p-[0-9a-f]{12}$/
at
required

With an offset; more than five minutes ahead of the server is refused.

string format: date-time
route
required
string
<= 64 characters
rules
required

The rules hash of the routes the pick used.

string
<= 16 characters
target
required

<harness>/<model>.

string
<= 128 characters
effort
required
string
<= 16 characters
reason
required
string
<= 256 characters
state
required
string
<= 16 characters
size
required
string
<= 16 characters
preview

Set when the pick was made for a preview model version’s trial cell.

boolean
run

Ties the picks of one cascade (draft

string
/^r-[0-9a-f]{12}$/
judges

The draft pick this pick judges. Not together with escalates.

string
/^p-[0-9a-f]{12}$/
escalates

The draft pick this pick escalates. Not together with judges.

string
/^p-[0-9a-f]{12}$/

Example

{
"org": "northwind",
"records": [
{
"type": "pick",
"id": "p-1a2b3c4d5e6f",
"at": "2026-10-29T07:42:00Z",
"route": "review.code",
"rules": "8ff065e",
"target": "codex/gpt-6.1-sol",
"effort": "high",
"reason": "primary",
"state": "ready",
"size": "medium"
},
{
"type": "verdict",
"id": "p-1a2b3c4d5e6f",
"at": "2026-10-29T08:10:00Z",
"verdict": "accepted"
},
{
"type": "council",
"id": "c-1a2b3c4d5e6f",
"at": "2026-10-29T08:20:00Z",
"route": "review.council",
"run": "r-1a2b3c4d5e6f",
"round": 1,
"rounds": 1,
"gate": "block",
"next": "revise",
"quorum": 2,
"block": "P1",
"reviewers": [
"codex/gpt-6.1-sol",
"claude/claude-opus-5-5"
],
"groups": {
"P0": 0,
"P1": 1,
"P2": 0,
"P3": 2
},
"findings": {
"codex/gpt-6.1-sol": 2,
"claude/claude-opus-5-5": 1
}
}
]
}

Every record is accounted for.

Media typeapplication/json
object
accepted
required

Records stored

integer
skipped
required

Records refused: a verdict on a pick this organization does not have, a malformed record, an ID another member already used, a record dated ahead of the server, or a record type the app does not know.

integer

Example

{
"accepted": 2,
"skipped": 0
}

The request is malformed (bad_request); the message says what to send.

Media typeapplication/json
object
error
required

A stable code to branch on.

string
Allowed values: missing_api_key invalid_api_key ip_not_allowed insufficient_scope rate_limited not_found internal_error
message
required

A readable explanation. Its wording may change.

string

Example

{
"error": "bad_request",
"message": "Name the organization: ?org=<organization ID>."
}

The key is missing (missing_api_key) or invalid, expired or revoked (invalid_api_key). The WWW-Authenticate header names the Bearer scheme.

Media typeapplication/json
object
error
required

A stable code to branch on.

string
Allowed values: missing_api_key invalid_api_key ip_not_allowed insufficient_scope rate_limited not_found internal_error
message
required

A readable explanation. Its wording may change.

string

Example

{
"error": "invalid_api_key",
"message": "The key is invalid, expired or revoked."
}
WWW-Authenticate
string
Example
Bearer realm="cedr.dev"

The key’s owner is not a member of that organization (not_a_member).

Media typeapplication/json
object
error
required

A stable code to branch on.

string
Allowed values: missing_api_key invalid_api_key ip_not_allowed insufficient_scope rate_limited not_found internal_error
message
required

A readable explanation. Its wording may change.

string

Example

{
"error": "not_a_member",
"message": "You are not a member of an organization named northwind."
}

The body is over 1 MiB (too_large); send at most 500 records a request.

Media typeapplication/json
object
error
required

A stable code to branch on.

string
Allowed values: missing_api_key invalid_api_key ip_not_allowed insufficient_scope rate_limited not_found internal_error
message
required

A readable explanation. Its wording may change.

string

Example

{
"error": "missing_api_key"
}

The key sent more requests in a minute than its owner’s plan allows (rate_limited): 30 on Free, 120 on Pro, 300 on Team and 600 on Business.

Media typeapplication/json
object
error
required

A stable code to branch on.

string
Allowed values: missing_api_key invalid_api_key ip_not_allowed insufficient_scope rate_limited not_found internal_error
message
required

A readable explanation. Its wording may change.

string

Example

{
"error": "rate_limited",
"message": "This key is over its plan's requests-a-minute limit."
}
Retry-After
integer

Seconds to wait before retrying.

Example
60

Something failed on our side (internal_error). Retry with backoff.

Media typeapplication/json
object
error
required

A stable code to branch on.

string
Allowed values: missing_api_key invalid_api_key ip_not_allowed insufficient_scope rate_limited not_found internal_error
message
required

A readable explanation. Its wording may change.

string

Example

{
"error": "internal_error",
"message": "Something went wrong on our side. Try again."
}