Errors, statuses and retries
The HTTP status of every failure, and what is safe to retry.
Your integration reacts correctly to each kind of failure.
INPUT_ERRORCorrect the inputsAUTH_ERRORRestore the connectionAPI_CHANGEDReview the repairEXECUTION_ERRORInspect the outcomeUnknown write outcome? Check the site before retrying.
Your path, in three steps.
- 01
Read the status and body
An invalid key returns a plain error field. A 429 refusal adds a message. These responses do not have an execution’s success field.
- 02
Address the cause before retrying
Correct the inputs, restore sign-in or wait for repair according to the code. An execution error does not guarantee that retrying is safe.
- 03
Never repeat an action with an unknown outcome
Check the record in the target software before sending anything again.
Want to go deeper?
Open just the topic you need.
Overview
Two things decide how your integration behaves when a connector fails: which of the four codes came back, and whether the request is safe to send again. This page covers both, and names the one case where retrying is the wrong thing to do even though everything looks retryable.
The status codes
| HTTP | When | Body |
|---|---|---|
200 | The connector ran and answered. | The envelope, with success: true. |
400 | An input is missing or the site rejected the value. | The envelope, error.code is INPUT_ERROR. |
400 | The connector is inactive or the request body is invalid. | { error, message }, with BRIDGE_NOT_ACTIVE or VALIDATION_ERROR. |
401 | The key is missing or wrong, or the connector could not act as the signed-in user. | A bare { "error": "…" } for a missing or wrong key; the envelope with AUTH_ERROR when the sign-in itself failed. |
403 | The account has reached an applicable usage or plan limit. | { error, message } with the limit reason. |
404 | The requested connector does not exist. | { "error": "NOT_FOUND", "message": "…" }. |
429 | Rate limit, or five executions already running for the account. | { "error": "TOO_MANY_REQUESTS", "message": "…" } — not the envelope. |
500 | Anything else: the site changed, a timeout, a transport failure. | The envelope, error.code is API_CHANGED or EXECUTION_ERROR. |
Some refusals do not use the execution envelope
A missing or invalid key returns { error }. Rate limits, inactive connectors, invalid request bodies and plan limits can return { error, message }, without success. Check the HTTP status and the shape of the body before reading an execution result.
The retry policy
| Code | Retry? | How |
|---|---|---|
INPUT_ERROR | No. | The same input fails identically. error.availableOptions often lists values that would work; error.sampleInput shows the shape expected. |
AUTH_ERROR | After access is restored. | Read the connector’s status and complete reconnection if requested. A pause alone does not establish that access is restored. |
API_CHANGED | After verification. | Review the repair status. Repair may be running or need your attention; wait for a verified result before calling again. |
EXECUTION_ERROR | Only when safe. | This code includes transient failures and runtime errors. For a read, a few attempts with increasing delays may help. For a write with an unknown outcome, stop and check the source site. |
429 | Yes. | Honour Retry-After when present. For the concurrency refusal there is no header: wait for your own in-flight calls to finish. Nothing is queued for you. |
The rule that matters more than the codes
Never retry an action whose outcome you cannot see
When a connector that changes something sent its request and the answer never arrived, nobody can tell a booking that failed from a booking your customer now holds. Vela treats that state as final: it starts no repair and retries nothing. Your integration must do the same — read the record on the site, then decide.
Vela does not guarantee write idempotency. Sending the same request twice can create two records. Do not assume that an idempotency header is supported unless the connector contract explicitly provides that behavior.
The fields worth reading
{
"success": false,
"data": null,
"error": {
"code": "INPUT_ERROR",
"message": "no practitioner named \"Ana\" on this account",
"availableOptions": ["Anna", "Anaïs"],
"sampleInput": { "practitioner": "Anna", "date": "2026-09-21" }
},
"meta": { "executionId": "9e600e25-…", "scriptVersion": 3, "durationMs": 412, "sessionRenewed": false },
"needsRepair": false
}-
meta.executionId— quote this in a support request. It identifies the run, its input and the version that answered. -
meta.scriptVersion— which version produced this result, so an old answer stays explainable. -
meta.sessionRenewed—truewhen Vela signed in again during this very call. Useful when you are debugging latency: that call paid for a sign-in. -
needsRepair— the error classification indicates a repairable failure. It does not prove that repair has started or succeeded; consult the connector status. An unknown write outcome blocks automatic repair.
Waiting for the answer
Every call answers with its result. ?async=true is accepted for older integrations and answered the same way. To watch the steps as they happen, use /stream.
What a failure does on Vela's side
- An
API_CHANGEDon a healthy connector starts one automatic repair. AnAUTH_ERRORmakes Vela sign in again once, then ask you. A write with an unknown outcome never starts anything automatic. - A
429, a timeout or a network failure changes nothing about the connector's state. Ambiguous failures are never held against it. - An
INPUT_ERRORis not a fault at all: the connector worked, the site said no.
The states this leads to are described in The life of a connector, and what happens next in How Vela repairs itself.
One server address for the tools in your toolset.
↗Your situation doesn’t match the guide?
Talk to the team ↗