API Reference
Errors
The error shape, the types, and the codes worth handling.
Every error has the same shape.
{
"error": {
"type": "invalid_request_error",
"code": "topic_not_found",
"message": "No topic with that id on this workspace. Check the value against your topics list.",
"param": "topic_id"
}
}
type— the broad category. Branch on this.code— the specific reason. Match on this when you need to.message— written for a human. Safe to log, not meant to be parsed.param— which field caused it, when one field did.
Match on code, never on message. Messages get reworded; codes do
not.
Types
| Type | Status | Meaning |
|---|---|---|
authentication_error | 401 | The key is missing, malformed or revoked |
permission_error | 403 | The key is valid but may not do this |
invalid_request_error | 400 | Something about the request is wrong |
not_found_error | 404 | No such resource on this workspace |
rate_limit_error | 429 | Too many requests. See rate limits |
A 5xx means the fault is ours. Retry with backoff.
Codes worth handling
contact_unsubscribed — you tried to create or update a contact
who has opted out. Nothing was changed. This is not a failure to work
around: see consent.
Skip them and carry on.
publishable_key_not_allowed — a publishable key reached
something other than /v1/subscribe. Almost always a secret key that
should have been used on the server.
origin_not_allowed — a publishable key was used from a domain it
is not allowed on. Add the domain, or check you have not shipped a
staging key.
topic_not_found — no such topic on this workspace. Also returned
for an archived topic, so a caller cannot probe which ids ever
existed.
topic_not_on_key_brand — the key is scoped to one brand and that
topic belongs to another.
invalid_limit — limit was outside 1–100. We refuse rather than
quietly clamp, so you find out now instead of wondering why page
sizes look odd.
invalid_cursor — the cursor was not one we issued. Pass
meta.next_cursor back exactly as given; do not construct one.
team_inactive — the workspace cannot act right now, usually
billing. See plans and billing.
Validation errors
A malformed body returns invalid_request_error with param naming
the field.
{
"error": {
"type": "invalid_request_error",
"code": "validation_failed",
"message": "email must be a valid email address.",
"param": "email"
}
}
Unknown fields are rejected rather than ignored. A typo’d frist_name
fails loudly instead of silently dropping the value.
Retrying
Retry 429 and 5xx with exponential backoff. Do not retry 4xx
otherwise — the request will fail the same way every time.
Creating a contact is idempotent on the email address, so a retry after a timeout merges rather than duplicating.
Updated at, Saturday, October 10, 2026