Agents don’t use APIs the way humans do. They retry aggressively, poll on loops, run unattended overnight, and make decisions off error messages nobody reads. Most APIs are designed for the human developer and merely tolerate the agent. This update flips that: we rebuilt the edges of the Synorb REST API around how automated callers actually behave.

Everything here is additive — existing integrations keep working byte-for-byte. Here’s what’s new.

Retries are now safe

Every runtime retries — networks blip, timeouts fire, orchestrators re-run steps. Write endpoints now accept an Idempotency-Key header: retry with the same key and you get the original response replayed — one Beacon, not two, no matter how many times your runtime fires the request. Reuse a key with a different payload and you get a clean 409 idempotency_conflict.

curl -X POST "https://api.synorb.com/v3/beacons" \ -H "api-key: $KEY" -H "secret: $SECRET" \ -H "Idempotency-Key: fed-watch-2026-07-08" \ -d '{"name":"Fed watch","status":"active"}' # run it twice — same beacon_id both times, no duplicate

The two 429s, finally distinguishable

“Slow down” and “you’re out of quota for the month” are completely different situations that call for completely different agent behavior. Both arrive as 429 — so existing handlers keep working — and the body carries a machine-readable error_code your agent can branch on, with Retry-After on both:

{"detail": {"error_code": "rate_limited", "retry_after_seconds": 60, ...}} ← wait, then retry {"detail": {"error_code": "quota_exhausted", "quota_resets_on": "2026-08-01", ...}} ← retrying won't help — upgrade or wait

The difference matters most in unattended loops: an agent that retry-loops on quota_exhausted burns cycles for three weeks. Now it can tell, and stop.

Polling is now free

Watching for changes is the most common agent pattern, so it should be the cheapest. Hot read endpoints (/streams, /plans, digest lists, /ontology/tags) now return an ETag. Send it back on your next poll; if nothing changed you get 304 Not Modified — no body, faster round-trip, and zero Manifest quota. A monitor that checks every five minutes now costs nothing on the quiet cycles.

curl -i "https://api.synorb.com/streams" -H "api-key: $KEY" -H "secret: $SECRET" \ -H 'If-None-Match: "synorb-abc123..."' # 304 Not Modified — nothing changed: no body, no quota

Honest pagination at the quota edge

On hard-cap plans, a page can land exactly where your remaining monthly quota runs out. When that happens, the response says so explicitly — quota_clamped: true, how many items were held back, and a recomputed pagination object — so your agent knows it hit the monthly limit mid-page instead of wondering where the data went.

A contract you can generate against

The published OpenAPI spec is now generated from the live route table on every change, with a CI gate that fails any release where spec and reality drift apart. It documents the complete customer surface — 79 endpoints — including typed schemas for the error codes above, the quota headers on every response, and the pagination envelope. The same contract, in agent-readable form, lives at /llms-full.txt and /agents.md.

And a promise to build against: the REST contract is unversioned-stable. We add fields and endpoints at any time; anything breaking ships with Deprecation and Sunset headers at least 90 days ahead, plus a changelog entry.

Available now, on every plan

All of this is live today for every plan, Starter through Enterprise, on api.synorb.com. Nothing to migrate, nothing to opt into — add an Idempotency-Key to your writes, branch on error_code, and switch your polls to conditional GETs whenever you’re ready. The full reference is in the docs under REST API → Errors & Rate Limits, Pagination, and Caching.

Built for the caller that never sleeps: retry-safe, quota-honest, and free to poll.