Handle errors and retry safely.
A refused request answers a 4xx or 5xx status with a body that names the
problem. A refused request runs nothing and costs no credits.
The error body
Every refused request answers application/json with one error object:
curl -i https://api.appenda.app/v1/tables/tbl_missing \
-H "Authorization: Bearer $APPENDA_API_KEY"
{
"error": {
"status": 404,
"code": "not_found",
"message": "The object doesn't exist in this workspace, or you can't see it. Check the id and the workspace.",
"param": null,
"docs_url": "https://appenda.app/docs/api/errors#not_found",
"request_id": "req_1888ee34e3fa99e71430e3072c54c339"
}
}
| Field | Meaning |
|---|---|
status | The HTTP status, repeated. |
code | A stable code. Branch on this, never on message. |
message | A sentence for people that says what to do. It can change. |
param | The request field at fault, or null. |
docs_url | This page, at the code's row. |
request_id | The request's id. Every response also sends it as X-Request-Id; quote it to support. |
A 401 also carries WWW-Authenticate, a 405 carries Allow, and a 429,
503 or 504 carries Retry-After.
Error codes
Every code the API can answer, with its status. 400 means the body cannot be
read; 422 means it can be read but a value is wrong. A 5xx means something
failed on our side: retry later.
| Status | Code | What it means and what to do |
|---|---|---|
| 400 | credentials_in_query | Send credentials in the Authorization header, never in the URL. Rotate a credential sent in a URL. |
| 400 | https_required | Use HTTPS for this request. Plain HTTP is refused instead of redirected. |
| 400 | idempotency_key_invalid | The Idempotency-Key header must be 1 to 200 visible ASCII characters. Fix the header and retry. |
| 400 | workspace_required | This key works across workspaces. Send the Appenda-Workspace header naming the workspace and retry. |
| 401 | unauthorized | The credential is missing, unknown, expired or revoked. Send a valid API key, or sign in again. |
| 402 | insufficient_credits | The workspace doesn't have enough credits for this run. Buy credits or lower the run's size, then retry. |
| 402 | subscription_inactive | The workspace's plan isn't active: its subscription is unpaid, paused or cancelled. Pay in Billing. The same key works again once the workspace reopens. |
| 403 | approval_required | Your role needs an Admin's approval for this action. Ask an Admin to approve the request. |
| 403 | forbidden | Your role or key can't call this operation in this workspace. Ask a workspace Admin for access. |
| 403 | instance_unavailable | This Appenda account isn't available. Contact support with the request id. |
| 403 | key_paused | This API key was paused because it spent far more than usual. A workspace admin can un-pause it. |
| 403 | plan_not_active | This workspace's plan isn't active. Renew or reactivate the plan in Billing, then retry. |
| 403 | plan_required | This workspace's plan doesn't include this feature. Upgrade the plan to use it. |
| 403 | plan_upgrade_required | This integration needs a higher plan. Upgrade the workspace's plan to use it. |
| 403 | region_not_offered | That data region isn't offered on this plan. Pick an offered region or upgrade the plan. |
| 403 | role_change_denied | Your role can't change Settings → Permissions. Ask a workspace Owner or Admin to make the change. |
| 403 | trial_card_required | People and Company Search on a trial need a card on file. Add a card in Billing, or choose a plan. |
| 403 | trial_search_limit_reached | This trial has used all its People and Company Search records. Choose a plan to keep searching. |
| 403 | workspace_limit_reached | Your plan's workspace limit is reached. Delete a workspace or upgrade the plan to create another. |
| 403 | workspace_mismatch | This key belongs to a different workspace. Use a key from this workspace, or drop the Appenda-Workspace header. |
| 404 | not_found | The object doesn't exist in this workspace, or you can't see it. Check the id and the workspace. |
| 404 | version_not_found | No version with this id exists for the table. List the table's versions to find the id. |
| 405 | method_not_allowed | This path doesn't serve that HTTP method. Use one of the methods in the Allow header. |
| 408 | request_timeout | The request body arrived too slowly. Send it again in one go. |
| 409 | admin_locked | The Admin role's permissions can't be changed. |
| 409 | approval_stale | The approved request no longer matches what would run. Send the request again for a new approval. |
| 409 | conflict | The request conflicts with the object's current state. Read the object again, then retry. |
| 409 | dedupe_too_many_matches | More than one record matches the dedupe key. Make the key unique or update the records by id. |
| 409 | dedupe_unchanged | The table already uses these dedupe settings, so nothing changed. |
| 409 | enrichment_already_running | A run is already in progress on this field. Wait for it to finish or stop it first. |
| 409 | enrichment_config_changed | The field this run uses was deleted or changed after you set up the run. Nothing ran. Set up the run again. |
| 409 | enrichment_paused | Enrichment is paused for this workspace. Resume it in the app, then retry. |
| 409 | field_key_conflict | A field with this key already exists with a different type. Choose another key or delete the old field. |
| 409 | idempotency_key_reused | This Idempotency-Key was already used for a different request. Use a new key for a new request. |
| 409 | import_fields_changed | The table's fields changed during the import. Start the import again. |
| 409 | import_finished | The import already finished. Start a new import to add more rows. |
| 409 | integration_connected | An integration with this name is already connected. Use the existing connection or remove it first. |
| 409 | job_finished | The run already finished, so there is nothing to stop. |
| 409 | monthly_spend_cap_exceeded | The workspace reached its monthly spend cap. Raise the cap in Billing or wait for the next month. |
| 409 | not_ended | The import is still running. Wait for it to end, then retry. |
| 409 | preview_stale | The table changed since the preview. Preview again, then confirm. |
| 409 | quote_mismatch | The import quote no longer matches the request. Ask for a new quote and confirm it. |
| 409 | role_limit_reached | The workspace has the most custom roles allowed. Delete a role before adding another. |
| 409 | role_name_taken | A role with this name already exists. Choose another name. |
| 409 | role_protected | Built-in roles can't be renamed or deleted. |
| 409 | route_model_unsupported | This workspace can't serve the request yet. Update the app and retry. |
| 409 | route_stale | The workspace moved to another region. Retry the request. |
| 409 | run_group_preview_stale | The run group changed since its preview. Preview the run group again, then start it. |
| 409 | schema_version_mismatch | The table's schema changed. Read the table again, then retry. |
| 409 | schema_version_stale | The action's parameters changed incompatibly since that schema_version. Read them again and rebuild the inputs. |
| 409 | selection_changed | The records you confirmed changed before the run started. Preview again and confirm the new count. |
| 409 | sole_owner | You're the only Owner of a shared workspace. Make someone else an Owner first. |
| 409 | spend_cap_exceeded | This run would spend more than its cap allows. Raise the cap or run fewer records. |
| 409 | table_changed | The table changed after the history entry named in if_unchanged_since. Read its history and try again. |
| 409 | transcript_head_mismatch | The transcript has moved on since these events were sent. Send them again from the head this error names. |
| 409 | upstream_not_run | A field this field reads has rows it has not run yet. Run that field first, or run both in one run group. |
| 409 | view_name_conflict | A view with this name already exists on the table. Choose another name. |
| 409 | workspace_create_conflict | A workspace is already being created with this request. Wait a moment, then list your workspaces. |
| 409 | workspace_delete_blocked | The workspace can't be deleted while it has an active subscription or other members. Resolve those first. |
| 409 | workspace_unprovisioned | The workspace has no cloud copy yet. Sync it from the desktop app first. |
| 410 | token_type_retired | Personal access tokens are retired. Create an API user token instead. |
| 413 | batch_too_large | Too much to write at once. Split the request into smaller batches. |
| 413 | fork_too_large | The version has more records than one copy allows. Copy a smaller table or version. |
| 413 | import_too_large | The file is larger than one import allows. Split it into smaller files. |
| 413 | payload_too_large | The request body is larger than the API accepts. Send a smaller body. |
| 413 | response_too_large | The answer would be larger than this operation can send. Ask for fewer records or fields. |
| 413 | restore_too_large | The restore would change more records than one restore allows. Restore a narrower version. |
| 413 | run_group_too_large | The run group has more fields than one run allows. Split it into smaller groups. |
| 415 | unsupported_media_type | The request body must be JSON. Send it with Content-Type: application/json. |
| 421 | residency_mismatch | The request reached the wrong data region. Send it to the workspace's own region. |
| 422 | agent_bot_limit_reached | Your plan holds at most this many bots in a workspace. Delete a bot or upgrade to add another. |
| 422 | agent_memory_limit_reached | Memory is full for this scope: delete some or keep fewer. |
| 422 | aggregate_formula_override | A chart can't override a formula field's calculation. Remove the override. |
| 422 | cell_invalid | A cell value doesn't fit its field; param names the field. Send a value the field accepts. |
| 422 | condition_must_be_boolean | A run condition must be a true/false formula. Change it to a formula that returns true or false. |
| 422 | constant_not_allowed | The input doesn't take that kind of value; param names it. Bind it the way its accepts list allows. |
| 422 | credential_not_applicable | This method runs inside Appenda and takes no credential. Leave the credential out. |
| 422 | credential_not_found | The credential doesn't exist for this provider in this workspace. List credentials and pick one, or leave it out. |
| 422 | credential_shape_unsupported | This provider doesn't take one pasted API key. Connect it in the desktop app instead. |
| 422 | credits_unavailable | This step's action can't run on Appenda credits; param names it. Use the workspace's own key. |
| 422 | custom_http_configuration_unsupported | HTTP request fields can't be created with an API key yet. Create them in the app. |
| 422 | custom_http_key_reentry_required | A Cloud HTTP request column's key must be saved again for this endpoint before it can run. Re-save the key in the app. |
| 422 | digest_mismatch | The uploaded content doesn't match its digest. Upload it again. |
| 422 | endpoint_not_allowed | The URL isn't allowed: use a public https address with no credentials in it. |
| 422 | enrichment_field_limit | The table already has as many enrichment fields as it allows. Delete one before adding another. |
| 422 | field_config_invalid | The field's config doesn't fit its type. Fix the config and retry. |
| 422 | field_conversion_required | This change rewrites the field's values. Send it again with convert set to true; a dry run shows what changes first. |
| 422 | field_conversion_too_large | The field holds too many values to convert in one request. Create a new field of the type you want instead. |
| 422 | field_limit_reached | The table has the most fields allowed. Delete a field before adding another. |
| 422 | field_not_found | A field the request names doesn't exist in this table. Check the field ids. |
| 422 | field_read_only | Formula and lookup fields are calculated, so their cells can't be written. Remove them from the request. |
| 422 | field_type_change_unsupported | A field can't change to that type. Create a new field of the type you want instead. |
| 422 | field_type_mismatch | A value doesn't fit its field's type. Send a value of the field's type. |
| 422 | field_type_unsupported | The API can't create or edit fields of this type. Use the desktop app for this field type. |
| 422 | force_confirmation_required | Re-running records that already have values needs confirmation. Send confirm_force: true with force: true. |
| 422 | formula_invalid | The formula doesn't parse or names a field that doesn't exist. Fix the formula and retry. |
| 422 | formula_read_only | A run condition written as a formula can only be read. Send it as rules. |
| 422 | interface_invalid | The interface definition isn't valid; param names the part at fault. Fix it and save again. |
| 422 | interface_limit_reached | The workspace has the most interfaces allowed. Delete an interface before adding another. |
| 422 | invalid_enrichment_config | The enrichment field's configuration isn't valid. Edit the field's settings and save again. |
| 422 | invalid_request | A value in the request breaks a rule; param names the field. Fix that value and retry. |
| 422 | invalid_shared_interface | This interface can't be shared as it is. Fix its blocks, then share it again. |
| 422 | invalid_waterfall | The waterfall isn't valid; param names the part at fault and reason says why. Fix it and save again. |
| 422 | matching_import_requires_atomic_batch | An import that matches existing records must run as one batch. Send it without chunking. |
| 422 | matching_import_requires_existing_fields | An import that matches existing records can only use fields the table already has. Map columns to existing fields. |
| 422 | max_credits_per_row_required | A step spends Appenda credits, so the waterfall needs max_credits_per_row. Add a cap. |
| 422 | missing_one_of | None of an input group's alternatives is given. Add one set of inputs from requires.one_of. |
| 422 | missing_required | A required input is missing; param names it. Add it and retry. |
| 422 | no_eligible_rows | No records match what the request selects. Change the filter or selection. |
| 422 | numeric_overflow | A number is outside the range its field can hold. Send a smaller number. |
| 422 | operator_not_supported | A run_when rule uses an operator it can't use; param names the rule. Use a view filter operator. |
| 422 | output_path_empty | Name the output's path: a dotted path into the field's result, such as company.size. |
| 422 | own_key_required | This integration needs your own API key. Add the key in Settings → Integrations. |
| 422 | provider_key_refused | The integration refused your API key. Check the key in Settings → Integrations. |
| 422 | provider_not_available_in_cloud | This integration runs only in the desktop app. Run it from the desktop instead. |
| 422 | quote_invalid | The import quote is invalid or expired. Ask for a new quote. |
| 422 | region_required | Choose a data region for the new workspace. |
| 422 | role_invalid | The role's name or permissions aren't valid. Fix them and save again. |
| 422 | run_dependency_cycle | The run group's fields depend on each other in a loop. Remove one of the dependencies. |
| 422 | schema_version_required | Name the schema_version of the action parameters the inputs were built from. |
| 422 | selection_too_large | The run selects more records than one run allows. Select fewer records. |
| 422 | step_over_row_cap | Running every step up to this one could spend more than max_credits_per_row; param names the step. Raise the cap or drop the step. |
| 422 | stored_data_limit_reached | Your account is at its plan's stored data limit. Nothing was written or charged. Delete data or upgrade before adding more. |
| 422 | table_enrichment_cycle | This field would send rows back into a table that feeds it. Pick a target table outside the loop. |
| 422 | table_row_limit_reached | The table is at your plan's limit of rows per table. Nothing was written or charged. Delete rows or upgrade before adding more. |
| 422 | too_many_records | The selection exceeds max_records. Narrow the filter or increase max_records up to 1,000. |
| 422 | unknown_input | The action has no input with that key; param names it. Read the action's parameters and fix the key. |
| 422 | unsupported_interface_page | This interface page type can't be shared. Share a different page. |
| 422 | unsupported_method | This integration doesn't offer that action. Pick an action the integration lists. |
| 422 | usage_range_too_wide | The usage range is too wide. Ask for a shorter date range. |
| 422 | view_default_read_only | A table's default view can't be changed or deleted. Create a new view instead. |
| 422 | view_limit_reached | The table has the most views allowed. Delete a view before adding another. |
| 422 | wrong_type | A value doesn't fit its input's type or allowed values; param names the input. Send a value of that type. |
| 423 | write_fenced | Writes to this workspace are paused for maintenance. Retry after the Retry-After seconds. |
| 429 | admission_saturated | Too many runs are starting at once. Wait the Retry-After seconds, then retry. |
| 429 | invite_quota_exceeded | This workspace sent too many invitations recently. Wait a while, then send more. |
| 429 | key_credit_limit_exceeded | This API key reached its daily credit limit. Wait for older spend to age out, or raise the key's limit. |
| 429 | provider_rate_limited | The integration's service is rate-limiting your key. Wait the Retry-After seconds, then retry. |
| 429 | rate_limited | Too many requests. Wait the Retry-After seconds, then retry. |
| 429 | search_limit_reached | This plan's People and Company Search records are used up for now. They come back when the limit resets, or choose a larger plan. |
| 429 | too_many_jobs | Too many list imports or CSV imports are open in this workspace. Wait for one to finish or stop one, then retry. |
| 500 | internal | Something failed on our side. Retry; if it keeps failing, contact support with the request id. |
| 503 | commit_outcome_unknown | The write may or may not have been saved. Retry with the same Idempotency-Key to find out. |
| 503 | lock_not_available | Another write is changing this object right now. Retry in a moment. |
| 503 | provider_unavailable | The integration's service is unavailable. Retry later. |
| 503 | public_api_disabled | The public API is turned off for maintenance. Retry later. |
| 503 | region_unavailable | That data region can't take new workspaces right now. Retry later or pick another region. |
| 503 | service_unavailable | Sign-up verification is briefly unavailable. Retry in a minute. |
| 503 | shared_writes_disabled | Cloud writes are briefly turned off. Retry in a few minutes. |
| 503 | stop_unavailable | The run couldn't be stopped right now. Retry in a few seconds. |
| 503 | unavailable | The service is briefly unavailable. Retry after the Retry-After seconds. |
| 503 | workspace_create_unavailable | Workspaces can't be created right now. Retry in a few minutes. |
| 504 | query_timeout | The query took too long. Narrow the filter or ask for fewer records, then retry. |
Job and key limits
A too_many_jobs error means your workspace already has 50 list imports, or 50
CSV imports, waiting or running. Wait for one to finish or stop one, then retry
after the Retry-After seconds.
A key_credit_limit_exceeded error means this key reached its daily credit
limit. Wait for older spend to age out, or ask a workspace admin to raise the
key's limit. Repeated retries do not make more credits available.
A key_paused error means unusual spending paused this key. Ask a workspace
admin to review its activity and un-pause it before retrying.
Retry safely
Retrying the same request is safe. The identical request (same key, path and body) within 10 minutes gets the first answer back instead of running again.
Without an Idempotency-Key, creating a table, field or view that already
exists with the same definition returns the existing one. Starting a run on a
field whose identical run is still going returns that run.
To decide yourself, send an Idempotency-Key header with any 1 to 200 visible
characters. It is optional and recommended for retries. The same key always
returns the first answer, and a new key always acts, even under a name that is
already in use.
curl https://api.appenda.app/v1/tables \
-H "Authorization: Bearer $APPENDA_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: create-q4-accounts" \
-d '{"name": "Q4 target accounts"}'
If a request times out, list or get before you retry. The first attempt may have worked.
Get help
If many requests fail at once, check status.appenda.app
first. Otherwise email [email protected] and
include the request id from X-Request-Id.