Skip to content

Error Format

Every error response replaces the success envelope’s data field with a structured error object, designed to be actionable by both humans and automated/agent clients — not just an HTTP status code:

{
"success": false,
"error": {
"code": "survey_not_found",
"message": "No survey with id mlb8i9f2UZLhXwJA.",
"suggestion": "Call listSurveys to discover valid ids.",
"retryable": false,
"details": { "survey_id": "mlb8i9f2UZLhXwJA" }
},
"meta": { "timestamp": "2026-07-23T12:00:00Z", "requestId": "..." }
}
Field Meaning
code A stable, snake_case domain code. Branch your error handling on this, not on the HTTP status or the human-readable message.
message Human-readable description. Safe to log or display, not guaranteed to stay word-for-word identical across API versions.
suggestion An imperative hint at how to recover — often naming the exact endpoint to call next.
retryable Whether re-sending the same request might succeed without any change on your end (a transient condition), as opposed to a client error that will keep failing until you change the request.
details Structured, code-specific context (e.g. the offending field, or the resource ID that wasn’t found).

Every code the API can return, with what it means, whether to retry, and how to recover. status is listed only where it’s fixed; several codes are raised with a caller-supplied status and genuinely vary by context — noted as “varies” rather than guessed at.

Code Status Retryable Suggestion
survey_not_found 404 No Call listSurveys to discover valid survey ids.
survey_not_published varies No Publish the survey before distributing.
precondition_failed varies No Re-fetch the resource and retry with the new ETag.
rate_limited 429 Yes Back off and honor the Retry-After header.
precondition_required varies No Include an If-Match header with the resource ETag.
export_not_found 404 No Call requestExport to create a new export job.
export_expired 410 No Export has expired. Request a new export.
invalid_status_transition varies No Check the current distributor status and allowed transitions.
resource_conflict 409 No A resource with this identifier already exists. Update or merge instead.
confirmation_required 409 No Re-call with the X-Confirmation-Token from error.details — see The confirmation gate below.
dependency_not_ready 409 Yes The prerequisite is not ready. Poll the status endpoint and retry.
tenant_unresolvable varies No The workspace could not be resolved. Verify the token is valid.
insufficient_scope 403 No Token lacks the required scope. Request a token with the needed scope.
job_timeout varies Yes The job timed out. Retry the request.
validation_failed 422 No Fix the fields listed in details and re-submit.
missing_parameter varies No Add the required parameter.
invalid_parameter 400 No Check the allowed values for this parameter.
quota_exceeded varies No Plan or usage limit reached. Upgrade to continue.
feature_locked 403 No Your plan does not include this. Upgrade to the plan in details.required_plan_name, or request something your plan includes.
service_unavailable 503 Yes Service is temporarily unavailable. Retry with exponential backoff.
internal_error 500 Yes An internal error occurred. Retry with backoff.
survey_locked varies Yes Survey is being edited. Wait for the lock to clear and retry.
invalid_token 401 No Token is malformed, revoked, or expired. Mint a new token.
missing_token varies No Provide an Authorization: Bearer header with a valid token.
token_expired varies No Rotate the token via the tokens API.
not_found 404 No Call the schema endpoint to discover available API operations.
method_not_allowed 405 No Check the allowed HTTP methods for this endpoint.
invalid_json varies No Request body is not valid JSON.
workspace_required 400 No Specify a workspace the token can access.
cross_tenant_denied varies No Resource belongs to another tenant. Do not retry.
agent_suspended varies No This agent is suspended or revoked. Contact the sponsor.
structure_change_requires_version varies No Create a new survey version before editing a published structure.
subscription_not_found 404 No Call listEventSubscriptions to discover valid subscription ids.
delivery_not_found 404 No Call listEventDeliveries to discover valid delivery ids for this subscription.
duplicate_subscription 409 No This workspace already has a webhook for that URL. Update it instead (details.subscription_id).
subscription_limit_reached 409 No A workspace can have 20 subscriptions. Delete one you no longer use.
subscription_is_pull 409 No This subscription has no URL (it is an event feed). Read it with listEvents, or create a webhook with a URL.
subscription_not_active 409 No Set the subscription back to active with updateEventSubscription, then retry.
delivery_pending 409 No This delivery is already queued. Wait for it to be sent.

This isn’t guaranteed to be the full list forever — new domain codes are added as the API surface grows. Always check retryable rather than assuming based on the code name alone.

For any non-GET request that includes an Idempotency-Key header, retrying the exact same request (same key, same body, same X-Workspace-ID and query string) is always safe — the API returns the original result rather than re-applying the write. Without that header, only retry requests where error.retryable is true.

Two requests are the exception, because their responses contain a signing secret, which we don’t keep: creating a webhook (createEventSubscription) and rotating its secret (rotateEventSubscriptionSecret). Neither stores its response. Retrying a create returns 409 duplicate_subscription naming the webhook that already exists; retrying a rotation rotates again. See Webhooks and Events.

A small number of high-stakes, hard-to-reverse actions (for example, anonymizing a contact, or exporting a dataset containing full PII) require an extra confirmation step rather than executing immediately:

  1. Call the action normally. Instead of executing, the API responds with 409 confirmation_required and a one-time confirmation_token in error.details, describing the action’s impact.
  2. Re-send the identical request with an X-Confirmation-Token header carrying that token. The action now executes.

The token is single-use, expires after a few minutes, and is bound to the exact request payload — it can’t be reused for a different action or a modified body.

Send Accept: application/problem+json on any request to get errors shaped as RFC 9457 Problem Details instead of the envelope above — useful if your HTTP client or framework already has built-in Problem Details support:

{
"type": "https://www.surveyrock.com/support/developers/errors#survey_not_found",
"title": "Survey not found",
"status": 404,
"detail": "No survey with id mlb8i9f2UZLhXwJA.",
"instance": "/api/v1/surveys/mlb8i9f2UZLhXwJA",
"code": "survey_not_found",
"suggestion": "Call listSurveys to discover valid ids.",
"retryable": false,
"requestId": "req_abc123def456",
"details": { "survey_id": "mlb8i9f2UZLhXwJA" }
}

type links back to this page’s anchor for the code. code, suggestion, retryable, requestId, and details are the same fields described above, carried as RFC 9457 extension members — nothing is lost by opting in.

This is purely additive: without that Accept header (the default, and every example elsewhere on this page), you get the envelope shape above, unchanged. Use whichever shape fits your client — both are fully supported.