Conventions
Six rules that hold everywhere in this API. Each one is here because somebody asked it with the whole specification open, which makes it a line that was missing rather than a line that went unread.
An organization takes an id or a name
/api/orgs/{orgId}/… accepts the uuid or the slug. They are interchangeable on every route.
GET /api/orgs/forumulate-sandbox/spaces
GET /api/orgs/4766b8a4-57da-48f0-91ff-c46da0f8d205/spacesA name that is not yours answers 404 — the same as a name that does not exist. An organization you cannot see is one you cannot learn the existence of.
A collection is keyed after its path
/spaces answers { spaces }. /webhooks answers { webhooks }. /orgs answers { orgs } — and { organizations } as well, which is what it has always answered and always will.
State your version and you get the envelope
Forumulate-Version: 2026-09-01With it, every collection carries page (limit, has_more, next_cursor, total) and a walk also carries sync.checkpoint. Without it you get the pre-envelope shape, plus Deprecation and Sunset headers saying how long that lasts.
page.limit is the limit that was applied, not the number of rows returned. A final page shorter than the limit is ordinary; has_more is the only thing that decides whether to keep walking.
Rate limits are per minute
Every response:
RateLimit-Limit: 600
RateLimit-Policy: 600;w=60
RateLimit-Remaining: 597
RateLimit-Reset: 41RateLimit-Policy names the window in seconds, so nobody has to infer it. A 429 also carries Retry-After.
Removals stay discoverable for 30 days
?include_deleted=true carries tombstones — deleted: true with the id and slug. A client on any sane cadence sees every removal without falling back to a full list. Longer than 30 days away, reconcile with a full list.
Deliveries are signed with the X- prefix
X-Forumulate-Signature: v1,sha256=<hex>
X-Forumulate-Timestamp: 1755490000
X-Forumulate-Event: page.published
X-Forumulate-Delivery: dl_01J…HMAC-SHA256 over ${timestamp}.${rawBody}, hex. X-Forumulate-Delivery is the key to deduplicate on. See Verifying a webhook.