Error catalog
Every problem code the platform emits, with its HTTP status, meaning, and retry semantics.
Every error response is an RFC 7807 application/problem+json body. Its type URL is https://docs.redspotinteractive.com/errors.html#{code} — the fragment is the same stable machine code carried in the body. Branch your handling on code, never on the human-readable title or detail. API-emitted errors also carry a requestId (echoed in the X-RSI-TRACE-ID response header) — quote the requestId on any escalation.
Several codes share an HTTP status — four distinct 409s, three 403s. The status alone is never enough to pick a recovery path; always read code.
Authentication & authorization
unauthorized 401 #
The request carried no bearer token, or the token is invalid or expired.
Retry: request a fresh token via the OAuth2 client-credentials grant and retry once. If it recurs with a fresh token, the credentials themselves are wrong — escalate rather than loop.
forbidden_scope 403 #
The token is valid but lacks the scope this endpoint requires.
Retry: no — retrying the same call with the same grant will always fail. Check GET /api/capabilities for the scopes you actually hold, and don't offer actions outside them.
forbidden_tenant 403 #
The token's practice could not be resolved. This is a configuration problem on the credential, not a transient fault.
Retry: no — escalate to RSI support quoting the requestId of the failing response.
quota_exceeded 403 gateway #
The client's 30-day call quota is exhausted. Emitted by the API gateway before the request reaches the API, so the body carries no requestId.
Retry: not until the 30-day quota window rolls over. If you hit this in normal operation, contact RSI to review the quota — retrying sooner only burns the next window.
Resources & request state
not_found 404 #
The resource doesn't exist or belongs to another practice — the API never reveals which. Ids are practice-scoped: a valid-looking id from elsewhere still returns 404 here.
Retry: only after re-running the lookup that produced the id (directory endpoints, patient match). Never substitute a different resource on the caller's behalf.
conflict 409 #
The request contradicts the resource's current state — for example, acting on an appointment that has already been cancelled.
Retry: re-read the resource, reconcile your intent with its current state, then retry if still applicable.
validation 400 / 422 #
One or more parameters are invalid or missing — the errors[] array names each offending field with a rule and message. A 400 means a value could not be read at all (wrong type or format, e.g. a datetime where a yyyy-MM-dd date is expected; rule is Binding); a 422 means readable values failed validation. Also returned as a 422 when the practice's scheduling system declines a write; detail then carries its reason.
Retry: after fixing the named fields. An unchanged resend returns the same 422.
The confirmation protocol
confirmation_required 428 #
Not an error — the first phase of the two-phase write protocol. The body carries a confirmationToken, an expiresAt, and a readBack of what the API parsed.
Retry: resend the byte-identical body plus the confirmationToken to the same URL before it expires. That confirming request performs the action.
confirmation_mismatch 409 #
The confirming request's body differs from the challenged intent. Nothing executed; the original challenge stays valid until it expires.
Retry: start over — issue a fresh challenge (no token) with the corrected body, then confirm that.
confirmation_incomplete 409 #
The confirmation token was already consumed but the outcome of that attempt is unknown — the write may or may not have happened.
Retry: not blindly. Verify current state via the read endpoints first; only start a fresh challenge if the action truly didn't happen.
slot_unavailable 409 #
The requested slot was taken between search and booking.
Retry: re-run GET /api/slots, offer the next available times, and book the newly chosen slot. Resending the original booking will keep failing.
Rate limiting
rate_limited 429 gateway #
The client exceeded its per-minute rate cap. Emitted by the API gateway before the request reaches the API, so the body carries no requestId.
Retry: yes — wait the number of seconds in the Retry-After response header, then resend. Back off rather than hammering; sustained 429s mean your call pattern needs batching or pacing.
Server & upstream
internal 500 #
An unexpected server error — or, on a write, the mutation's audit record could not be persisted, in which case the write may have executed upstream even though no success was returned.
Retry: for a read, once after a short pause, then escalate with the requestId. For a write, do not assume it failed — verify current state via the read endpoints before retrying, or you risk a duplicate.
upstream_unavailable 502 #
The practice's scheduling system could not be reached and the request was not executed.
Retry: yes — transient by nature; wait the Retry-After seconds and resend the same request. A confirmed intent's confirmationToken stays valid — reuse it. If it persists, escalate with the requestId.
capability_not_supported 503 #
The practice's scheduling system cannot perform this operation at all — a vendor capability gap, not an outage.
Retry: never at this practice — the same call will never succeed here. Tell the caller it can't be done through this channel and hand off.