warden

API reference

Errors

One shape for every error, the codes to branch on, and how a refused record differs from a refused request.

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

CodeStatusMeaning
invalid_key401The key is missing, malformed, unknown or revoked. Warden does not say which.
invalid_request400The body, a parameter or a cursor is not one Warden accepts.
not_found404No such record, or no such member for this key's provider.
scope_not_granted403The key is real and was not granted what this route needs. scope names it. Make a key with that scope.
plan_not_allowed403The 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_limited429The key is over its allowance. Wait for the Retry-After header, in seconds.
unavailable503Warden cannot do this right now. Try again later.
internal500Something went wrong on Warden's side.

These belong to the signed-in step in Make a key. A key never sees the first three:

CodeStatusMeaning
unauthenticated401No credentials, or a person's session that has ended.
forbidden403The person is not an admin of that organization.
organization_required400The request did not name an organization with X-Warden-Org.
plan_not_allowed403The 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:

CodeStatusMeaning
refused409The 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.