RSI Public Platform API / Documentation / Error catalog

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.

CodeHTTPRetry?
unauthorized401Once, with a fresh token
forbidden_scope403No
forbidden_tenant403No — escalate
quota_exceeded403Not until the quota window resets
not_found404Only after a fresh lookup
conflict409After re-reading state
confirmation_mismatch409Start a fresh challenge
confirmation_incomplete409Verify via reads first
slot_unavailable409Re-search, then book another slot
validation422After fixing the named field
confirmation_required428Not an error — confirm
rate_limited429Yes, after Retry-After
internal500Reads: once. Writes: verify first
upstream_unavailable502Yes, after a pause
capability_not_supported503Never, at this practice
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.