Handle GOFA API errors, rate limits, retries, and session charges.
The API uses JSON responses and includes a GOFA-generated x-request-id on
authenticated requests. Keep that ID with your application logs and include it
when contacting GOFA Support; do not send API keys or health data to Support.
Errors from the common API access boundary have this shape:
{
"error": {
"code": "<public-error-code>",
"message": "<human-readable-message>",
"requestId": "<GOFA-generated-request-id>"
}
}Some handler errors contain only error.message and error.requestId; do not
assume every error has a code. The common access boundary and new session
creation errors use the coded responses listed below.
| Status | Code | Meaning |
|---|---|---|
400 | invalid_request | The JSON body, userId, locale, or returnUrl is invalid. |
400 | IDEMPOTENCY_KEY_INVALID | The required session-creation key is missing or malformed. |
400 | return_origin_not_approved | The returnUrl origin is not approved for the workspace. |
401 | invalid_api_key | The bearer key is missing, invalid, expired, or revoked. |
402 | INSUFFICIENT_CREDITS | The initialized shared account lacks the full product price for a new session. |
402 | insufficient_credits | Legacy allowance is insufficient for a new session. Legacy responses may include cycle details. |
409 | IDEMPOTENCY_CONFLICT | An idempotency key was used with a different request for the same workspace and product. |
403 | product_not_available | Vital Scan is not available for the workspace. |
403 | product_not_entitled | The authenticated key is not entitled to Vital Scan. |
403 | entitlement_inactive | The workspace's commercial entitlement is not active. |
403 | entitlement_expired | The workspace's commercial entitlement has expired. |
403 | free_membership_required | The workspace does not have the required Free membership. |
429 | rate_limit_exceeded | The configured requests-per-minute limit was exceeded. |
503 | entitlement_unavailable | Entitlement data could not be read or validated temporarily. |
503 | rate_limit_unavailable | Request-protection configuration or its distributed check is temporarily unavailable. |
503 | free_membership_unavailable | Free membership data is temporarily unavailable. |
503 | free_allowance_unavailable | Free allowance data is temporarily unavailable. |
503 | vital_scan_creation_unavailable | GOFA could not confirm whether session creation committed. |
503 | commit_outcome_unknown | The session-creation transaction outcome is unknown. |
503 | usage_event_unavailable | Successful commercial usage recording is temporarily unavailable. |
500 | vital_scan_creation_failed | An unexpected error prevented session creation. |
The product_not_available and invalid_request checks are specific to
session creation. Authentication, workspace access, and rate-limit checks also
apply to GET. The public single-session paths have POST and GET operations;
DELETE returns 405 Method Not Allowed.
400, 401, or 403.402, wait until allowance is available or ask GOFA to update the
workspace; repeating the request cannot create a session without enough credits.409 IDEMPOTENCY_CONFLICT, choose a new key only if you intend a distinct
assessment. A retry of the original assessment must use the original body.429, honor Retry-After and use exponential backoff. A GET that polls
the same session too soon returns 429 with the number of seconds to wait.5xx, with
bounded exponential backoff. Honor Retry-After if present and stop at a
deadline rather than polling indefinitely.5xx), retry
with the same Idempotency-Key and identical request body. If the first
transaction committed, the replay returns 200 OK with the original session
and Idempotency-Replayed: true, without a second charge. If it did not commit,
a successful retry returns 201 Created. Replaying never extends the original
two-hour hosted window. Use bounded backoff; retain the key, request time,
and GOFA request ID for support. Do not send keys, hosted URLs, or health
data to Support.Authenticated responses may include:
ratelimit-limit — the workspace-configured requests-per-minute limit.ratelimit-remaining — the remaining requests in the current window.ratelimit-reset — the reset time as a Unix timestamp in seconds.Retry-After — how long to wait when a request is rejected for a retryable
limit condition.The exact requests-per-minute value is configured per workspace; do not hardcode a plan value in your client. GET is not billable, but still consumes rate-limit capacity. In addition, a given session may be polled no more than once every five seconds.
These are separate limits. For example, five-second polling is 12 requests per minute, so it exceeds a workspace configured for 10 requests per minute even with only one session. Share one request budget across all keys, session polls, and creates in the workspace. See the bounded, single-session example in Quick Start.
A first successful POST to either hosted assessment product (201 Created)
consumes that product's configured price in credits. An idempotent 200 OK
replay and GET consume no credits. A rejected request does not intentionally
create a billable session. Letting a session expire or leaving the hosted flow
does not refund the creation charge.