Every error answer has the same shape:
{ "error": "A sentence for a person.", "code": "invalid_key" }Branch on code. The sentence can be reworded at any time and the code cannot. New codes may be
added, so treat one you do not know by its HTTP status.
Codes
| Code | Status | Meaning |
|---|---|---|
invalid_key | 401 | The key is missing, malformed, unknown or revoked. Warden does not say which. |
invalid_request | 400 | The body, a parameter or a cursor is not one Warden accepts. |
not_found | 404 | No such record, or no such member for this key's provider. |
scope_not_granted | 403 | The key is real and was not granted what this route needs. scope names it. Make a key with that scope. |
plan_not_allowed | 403 | The organization's plan does not allow this. capability names what was refused. Of this reference, only Create an enrollment invitation answers it. A sync reports a plan refusal per record, in rejected, and not as a 403. |
rate_limited | 429 | The key is over its allowance. Wait for the Retry-After header, in seconds. |
unavailable | 503 | Warden cannot do this right now. Try again later. |
internal | 500 | Something went wrong on Warden's side. |
These belong to the signed-in step in Make a key. A key never sees the first three:
| Code | Status | Meaning |
|---|---|---|
unauthenticated | 401 | No credentials, or a person's session that has ended. |
forbidden | 403 | The person is not an admin of that organization. |
organization_required | 400 | The request did not name an organization with X-Warden-Org. |
plan_not_allowed | 403 | The organization's plan has no room for another key. capability is integration_keys. |
One more code exists elsewhere in Warden, and is listed so that a client can name it. No endpoint in this reference returns it:
| Code | Status | Meaning |
|---|---|---|
refused | 409 | The request was well formed, and Warden will not do it. |
What a 400 tells you
A sync whose body cannot be read says what was wrong in error:
{ "error": "Send at most 500 records in one request.", "code": "invalid_request" }An enrollment request whose body is wrong says only Invalid request. It names no field.
A refused record is not an error
A sync that Warden could read returns 200 even when some records were refused. Each refusal is in
rejected, with its reason in because. When refused is record, nothing of that record was
stored. When it is identity or identities, the record was written and only what is named was
not:
{
"records": [],
"rejected": [
{ "index": 0, "external_id": "member-1004", "refused": "record", "because": "normalized_state has to be active, inactive or unknown. warden never works it out from status." }
]
}because is a sentence, written to be logged and read by a person. Do not branch on it.
Handling a 429
if (res.status === 429) {
const seconds = Number(res.headers.get('Retry-After') ?? 60)
await new Promise((done) => setTimeout(done, seconds * 1000))
}A refused request writes nothing and is not counted against the next minute.