Conventions
The headers, idempotency rules, and pagination model that are constant across every Keepable API. Learn them once.
A handful of conventions hold across the Keepable Sender API. Learn them once here and the per-endpoint guides stay short.
Base URL
The Sender API is served from:
https://api.keepable.co/sender/v2Paths in this documentation are written relative to it.
API version
The v2 in that URL is the version. There is nothing else to set: no header, no
date to keep in step with a release, nothing to get wrong.
The public v2 contract is currently a pre-launch preview. There are no external production integrators yet, so its operation set may still be narrowed before general availability. The changelog will announce when the compatibility commitment begins. After that point, breaking changes use the normal major-version and sunset process.
One thing we ask in return: handle values you have not seen before. New kinds of correspondence, new states and new document types arrive inside a version, so a client that treats an unfamiliar enum value as a fatal error will break on something we added for somebody else. Keep unknown values rather than rejecting them.
Idempotency
Most mutating operations accept an Idempotency-Key header, a
client-generated string of 1-255 visible ASCII characters, unique per logical
operation. Send one on any create you want to be safely retryable (content
delivery requires it); a UUID is the natural choice:
Idempotency-Key: 9f1c8e2a-7b3d-4f10-9a2e-6c5b4d3e2f1aThis makes retries safe. If a request times out and you do not know whether it landed, replay it with the same key:
- Same key, identical request fingerprint → you get the original result back, and the operation runs exactly once.
- Same key, different body → the API rejects the replay with
409 Conflict, protecting you from accidentally double-charging the key for a different payload.
Generate one key per logical action and persist it before you make the call, so a crash-and-retry reuses the same key. Generating a fresh key on every attempt defeats the protection: each attempt looks like a new operation.
Pagination
List endpoints return a page plus an opaque cursor, and take two query parameters:
| Parameter | Meaning |
|---|---|
limit | Page size, 1-200. Defaults to 50. |
cursor | Opaque cursor from the previous response's next_cursor. |
Every list response carries a next_cursor. When it is null, you have reached
the end.
# First page
GET https://api.keepable.co/sender/v2/correspondence?limit=50
Authorization: Bearer {{KEEPABLE_TOKEN}}
###
# Next page: pass the previous next_cursor as `cursor`
GET https://api.keepable.co/sender/v2/correspondence?limit=50&cursor=eyJvZmZzZXQiOjUwfQ
Authorization: Bearer {{KEEPABLE_TOKEN}}Draining the whole list is the same two calls in a loop:
async function* allCorrespondence(token: string) {
let cursor: string | undefined;
do {
const url = new URL("https://api.keepable.co/sender/v2/correspondence");
url.searchParams.set("limit", "100");
if (cursor) url.searchParams.set("cursor", cursor);
const res = await fetch(url, {
headers: {
Authorization: `Bearer ${token}`,
},
});
const page = await res.json();
yield* page.correspondence;
cursor = page.next_cursor ?? undefined;
} while (cursor);
}A cursor is opaque and short-lived. Do not parse one, store one, or build a permalink from one: it encodes a position in a result set that is still moving.
Rate limits
Responses carry RateLimit-Limit, RateLimit-Remaining, and RateLimit-Reset.
Read them rather than discovering the ceiling by hitting it, and back off on
429 using RateLimit-Reset
rather than a fixed sleep.
Errors
Every non-2xx response is an RFC 7807 application/problem+json document. The
full catalogue (every problem type, what triggers it, and whether to
retry) is in Errors.
Authentication
Use an organisation-scoped API key from your backend. Its prefix selects the data plane and its seven-scope vocabulary limits what the integration may do.
Errors
Every non-2xx response is an RFC 7807 problem document. Here is the full catalogue of problem types, what triggers each, and whether to retry.