Developer docs
Rate limits & idempotency
Per-key rate limits with standard headers, and Idempotency-Key support so retries never double-send.
Two request-level features make automating against the API safe: rate limits tell you how much budget you have, and idempotency lets you retry sending endpoints without sending twice.
Rate limiting
Each API key is limited to 300 requests per 60 seconds (5 req/s sustained). Every response carries the current budget:
| Header | Meaning |
|---|---|
X-RateLimit-Limit | The per-window ceiling. |
X-RateLimit-Remaining | Requests left in the current window. |
X-RateLimit-Reset | Seconds until the window frees a slot. |
Going over the limit returns 429 Too Many Requests with a Retry-After header (in seconds).
Wait that long, then retry.
The AI endpoint has its own, much lower limit
POST /conversations/:id/ai-draft is additionally
limited to 20 requests per 60 seconds per key, reported under its own headers so the general budget
above stays readable:
| Header | Meaning |
|---|---|
X-RateLimit-AI-Limit | The per-window ceiling for AI calls. |
X-RateLimit-AI-Remaining | AI calls left in the current window. |
X-RateLimit-AI-Reset | Seconds until that window frees a slot. |
It is separate because this endpoint spends money, not just a database round trip. At the general ceiling a runaway loop would bill three hundred generations a minute while looking completely healthy — every request valid, nothing to alarm on. Your workspace AI budget is still the real backstop; this is the earlier, cheaper one.
Both limiters run on an AI request, and the stricter one bites first.
Idempotency
Creating a conversation and replying both send email, so a blind retry after a network blip could
send twice. Prevent that by sending an Idempotency-Key header — any string unique to the
operation (a UUID is ideal):
POST /conversations/:id/reply
Authorization: Bearer rp_live_…
Idempotency-Key: 3f9c1e7a-9b2d-4c8e-a1f0-6d5b4c3a2e10
Content-Type: application/json
- The first request runs normally; its response is stored against the key.
- A retry with the same key and body replays that stored response — no second email.
- Reusing the key with a different body returns
409 Conflict. - A retry sent while the first is still in flight returns
409— wait, then retry.
Keys are remembered for 24 hours. A request without the header is processed normally (not deduplicated), so always send one whenever a retry is possible.
Recovery after an interrupted request (next grouped release)
For conversation creation and replies, an accepted message source is retained with the
idempotency reservation. If the connection is lost before a response is saved, retry using
the same key and unchanged body. The retry can return the existing conversation and its
current message state without creating or sending another message. A conversation response
can contain queued or uncertain delivery; check the message’s deliveryState before treating
it as delivered.
Completed responses keep the normal 24-hour replay window. An unresolved committed source
is retained beyond that window until reconciled. A missing retained message returns 409
and is not recreated. A request that committed no source can safely retry after its reservation
is released or expires. Older uncertain requests without a recoverable source still return
409; inspect the conversation before starting a separate send.
A retained source does not imply successful provider delivery; inspect the existing outcome before taking further action.