Skip to content

API conventions

Send an Idempotency-Key header (any string you generate, unique per logical operation) on any mutating POST. A retried request with the same key returns the exact original response — byte-identical, not just logically equivalent — instead of creating a second resource. A duplicate request that arrives while the first is still in flight gets 409 idempotency_conflict; retry it once the first has actually completed.

Idempotency-Key: create-referral-order-4471

Keys are scoped to your tenant and never expire early — always generate a fresh one per genuinely new operation, not per HTTP attempt.

List endpoints use cursor pagination via starting_after, not page numbers — ids sort the same way they were generated (prefixed ULIDs), so a cursor is just “the last id you saw”:

GET /v1/patients?limit=20
GET /v1/patients?limit=20&starting_after=pat_01H8X...

Every list response has the same envelope:

{ "data": [ /* up to `limit` items */ ], "has_more": true }

limit defaults to 20, maximum 100.

Railflo pins your integration to a specific API version, Stripe-style — set once at signup (currently 2026-09-01, the only version that exists at P0, but the pinning mechanism is real from day one). Override per-request with:

Railflo-Version: 2026-09-01

The response always echoes back the version it was actually served under in a Railflo-Version response header, so you can confirm which shape you got.

Every endpoint is rate-limited per API key (token-bucket). A limited request gets 429 rate_limited with a Retry-After header telling you how many seconds to wait. Sandbox limits are the same as production — the sandbox is for testing your integration logic, not for load-testing Railflo itself.