Errors
The error body every Simple Feedback API route returns, every error code it can carry, and what your integration should do with each one.
Die Entwickler-Dokumentation gibt es nur auf Englisch, wie die API selbst.
Every failed /v1 request answers with the same body:
{
"error": {
"code": "INSUFFICIENT_SCOPE",
"message": "This API key is missing the \"insights:write\" scope",
"details": {
"requiredScope": "insights:write",
"grantedScopes": ["surveys:read", "responses:read"]
}
}
}codeis stable. Branch on it.messageis for people and may change. Don't parse it.detailscarries extra facts for some codes and is{}otherwise.
What to do with a status
| Status | Meaning | What to do |
|---|---|---|
400 |
The request doesn't validate | Fix the request; retrying won't help |
401 |
The key is missing, wrong, revoked or expired | Fix or replace the key |
403 |
The key works but isn't allowed this | Fix the scopes, the plan or the account |
404 |
The survey or venue doesn't exist for this key | Check the id |
409 |
A limit or state stops the change | Read the code; most need a person |
422 |
The Idempotency-Key was used for another request |
Use a new key for a new request |
429 |
Too many requests | Wait Retry-After seconds, then retry |
5xx |
Something failed on our side | Retry later with backoff |
Codes
Authentication
| Code | Status | When |
|---|---|---|
API_KEY_MISSING |
401 | No Authorization: Bearer header |
API_KEY_INVALID |
401 | The token is malformed, unknown, or its secret doesn't match |
API_KEY_REVOKED |
401 | The key was revoked, or the person who created it lost the access it acts with |
API_KEY_EXPIRED |
401 | The key is past its expiry date |
API_KEY_REVOKED doesn't say whether someone revoked the key or its creator left. The holder of a key isn't always
the person who created it, so nothing about that person is disclosed.
Permissions and plan
| Code | Status | When |
|---|---|---|
INSUFFICIENT_SCOPE |
403 | The key lacks the scope. details names the required and granted scopes |
API_ACCESS_NOT_IN_TIER |
403 | The organization's plan doesn't include API access |
ORG_SUSPENDED |
403 | The organization isn't active |
FEATURE_NOT_IN_TIER |
403 | The request uses a feature the plan doesn't include, for example custom branding |
INSUFFICIENT_VENUE_ROLE |
403 | A venue key reached for a survey or QR code of another venue |
EMAIL_NOT_VERIFIED |
403 | Publishing with PATCH while the key creator's email address isn't verified (details.reason is EMAIL_NOT_VERIFIED) |
Not found and validation
| Code | Status | When |
|---|---|---|
SURVEY_NOT_FOUND |
404 | No such survey, or it belongs to another organization |
QR_CODE_NOT_FOUND |
404 | No such QR code, or it belongs to another organization |
QR_CODE_NOT_A_SURVEY_CODE |
400 | surveyId sent for a redirect or menu code, which can't open a survey |
ACCESS_CODE_NOT_FOUND |
404 | The code sent to /access-codes/qr is not one of this survey's single-use codes |
VALIDATION_FAILED |
400 | The body or query doesn't validate; message names the first problem |
VENUE_OUTSIDE_ORG |
403 | The venueId you sent belongs to another organization |
UNSUPPORTED_IMAGE_TYPE |
400 | The uploaded image isn't PNG, JPEG or WebP |
INVALID_IMAGE |
400 | The uploaded bytes aren't a readable image |
Undo
| Code | Status | When |
|---|---|---|
NOTHING_TO_UNDO |
409 | No change made through the API or MCP is recorded for the survey; dashboard edits don't count |
SURVEY_CHANGED_SINCE |
409 | Someone edited the survey after the change you want to undo |
Plan copies
| Code | Status | When |
|---|---|---|
POLL_ALREADY_REPLACED |
409 | The survey was already replaced by a copy that fits the plan |
QR_CODE_NOT_ON_POLL |
400 | A code in qrCodeIds doesn't point at the survey |
Idempotency keys
| Code | Status | When |
|---|---|---|
IDEMPOTENCY_KEY_REUSED |
422 | The key was already used with a different body or on a different route |
IDEMPOTENCY_KEY_IN_PROGRESS |
409 | The first request with this key is still running; retry after details.retryAfterSeconds |
Limits
| Code | Status | When |
|---|---|---|
TIER_LIMIT_REACHED |
409 | A plan limit is full: active surveys, pages, languages and so on |
AI_QUOTA_EXCEEDED |
409 | The plan's AI quota for the period is used up |
ACCESS_CODE_LIMIT_REACHED |
409 | The survey would have more than 10 000 single-use codes |
FILE_TOO_LARGE |
413 | An uploaded image is over 2 MB |
RATE_LIMITED |
429 | Too many requests. details.retryAfterSeconds and the Retry-After header say how long to wait |
Don't retry 409 limits automatically. They clear when someone upgrades the plan, deactivates a survey, or a new
quota period starts.
Created but not published
Creating a survey with active: true creates it first and then publishes it. When publishing fails, for example
because the key creator's email address isn't verified, the survey still exists as a draft and the call answers
409 with its id. For an unverified email address the code is EMAIL_NOT_VERIFIED:
{
"error": {
"code": "EMAIL_NOT_VERIFIED",
"message": "Please verify your email before publishing polls. The survey was created as a draft and was not published.",
"details": {
"reason": "EMAIL_NOT_VERIFIED",
"createdSurveyId": "a1b2c3d4-0001-4001-8001-000000000001"
}
}
}Server errors
| Code | Status | When |
|---|---|---|
UPSTREAM_ERROR |
502 | A service we depend on failed, today only the AI provider |
INTERNAL_ERROR |
500 | A bug on our side |
Server errors always carry a generic message and empty details. The cause is recorded on our side; if you contact
support, tell us the time and the route.
The order of checks
Every request is checked in this order before the route runs. The first failure is what you get back:
Authorizationheader missing →401 API_KEY_MISSING- Token malformed, unknown or wrong →
401 API_KEY_INVALID - Key revoked →
401 API_KEY_REVOKED - Key expired →
401 API_KEY_EXPIRED - The creator is no longer active, no longer a member, or no longer has the access the key acts with →
401 API_KEY_REVOKED - Organization not active →
403 ORG_SUSPENDED - Plan without API access →
403 API_ACCESS_NOT_IN_TIER
Then the request body and query are validated (400 VALIDATION_FAILED), and only then does the route check the
key's scope (403 INSUFFICIENT_SCOPE) and do its work.