Errors & recovery
Recover from errors without duplicating work.
Never parse a message string to decide what to do. Branch on the HTTP status, error.code and retryable, and keep the request ID, document ID and idempotency key so you can recover or ask for help.
On this page
Find your error code
Select your error.code to jump to what to do. Each row explains its codes under “Code-specific guidance”. Got no response or no code? See the timeout and SDK rows later in the table.
Terminal processing error codes
These codes appear on a document whose status is failed. That status is final: retrying a GET or upload-complete does not restart processing. They are different from the SDK timing out while processing continues on the server.
What to do for each error
A needs_review result is not an error, and retrying does not change it. When required fields, line-item rules or duplicate checks send a result to review, read its checks and the document’s review policy settings.
| What happened | Retry? | What to keep or change | Document and billing |
|---|---|---|---|
Invalid request fields
| Not until you fix the rejected fields. Code-specific guidance
| error.details.issues lists the rejected fields when present. Fix them and send the create again. If the document already exists, keep its original request body and key instead of changing them. | The request was rejected, not your file: this does not mean the uploaded file is corrupt. Keep any document ID you already have. |
Invalid callback URL
| Fix the callback URL, or leave it out, and send the create again. Code-specific guidance
| Use a public HTTPS destination with no credentials or fragment. If a document already exists for this idempotency key, do not change its request body. | Only the callback was rejected; your file was not checked. Uploading the file again does not fix it. |
Invalid API key or forbidden operation
| No. Fix the API key or account access first. Code-specific guidance
| Do not upload invoice files until the key is accepted. | The rejected request created nothing. Documents you created earlier are unaffected. |
Billing required or Trial allowance exhausted
| After you fix billing or upgrade. Retry create with the same body and key, or retry upload-complete for the same document. Code-specific guidance
| After a billing error on upload-complete, do not upload the file again. If a document has already failed with this code, it cannot be resumed; trying again means a new document with a new key. | Rejected before processing, so not billed. Keep the document ID. A Trial never processes part of a document to fit the remaining allowance. |
File is too large
| Not with the same file. Send a file of 20 MiB or less. Code-specific guidance
| Declare the exact byte count. If the oversized file was already uploaded, create a new document for the smaller file; an uploaded file cannot be replaced. | This is about file size only. It is not a page-limit or Trial-allowance error. |
Unsupported or mismatched content type
| Use a PDF, JPEG, or PNG with the matching content_type. Code-specific guidance
| At create, correct content_type and send again. At upload-complete, the uploaded file cannot be replaced, so create a new document. | Renaming the file or changing its content type does not turn an unsupported file into a supported one. |
Invalid, corrupt, or unsupported file
| Not with the same file. Fix the file, or its declared size or type, first. Code-specific guidance
| A wrong size or type on an uploaded file needs a new document. Retrying does not fix corrupt or unsupported content, and a failed document stays failed. | No usable result. Files rejected before processing are not billed. A corrected file is a new submission with its own idempotency key. |
Upload window expired
| No. An upload_expired response means the upload window is closed, and retrying does not reopen it. Code-specific guidance
| If only the signed upload URL expired and the API has not returned upload_expired, replay create with the same body and key to get a fresh URL. Start a new submission with a new key only after upload_expired. Never change the key just because a request timed out. | The old upload URL no longer works, and no replacement document is created. If a create replay returns 200 awaiting_upload, call upload-complete first to check whether the file already arrived. |
File not received
| Yes: upload the complete file again while the signed upload URL is still valid. Code-specific guidance
| The API has confirmed the file is not in storage. Send only the headers returned with the upload URL, never your API key. | Use the same document and upload path; do not create a second document. |
Idempotency conflict
| Not with a changed body under the same key. Code-specific guidance
| Send the original create body again, or use a new key only if this is a genuinely different invoice. | A document may already exist for the original request. The conflict itself is not billed. |
Operation does not fit the document’s status
| Retrieve the document and choose the operation its current status allows. Code-specific guidance
| Keep the document ID. This code does not mean the file is missing: do not upload again or create a replacement because of it. | Processing may be underway or already finished. Polling a failed document does not restart it. |
Rate limit or service temporarily unavailable
| Yes, a limited number of times when retryable is true; treat Retry-After as a minimum delay. Code-specific guidance
| Retry the same request: the same create body and key, or upload-complete for the same document. A retried upload-complete is never a reason to upload the file again. | A document may already exist, so keep its ID and key. A 5xx status alone does not mean you may retry; check retryable. |
Internal error
| No automatic retry when retryable is false. An HTTP 500 alone does not mean you may retry. Contact support with the request ID if needed. Code-specific guidance
| Keep the document ID and the exact create body and key. A failed response does not prove that nothing happened, so do not replace the document or upload again outside the steps on this page. | A document or processing run may already exist. Retry the same request only when retryable is true. |
| Create timed out or returned no response | Yes: send the exact create body and idempotency key again. | Never change the key just because the response was lost or timed out. | The document may already exist; the replay returns it. |
| File upload outcome unknown | Call upload-complete before uploading again; it tells you whether the file arrived. | Upload again only after upload-complete returns upload_not_complete, and only while the signed upload URL is still valid. | Use the same document; the file may already be in storage. |
| upload-complete outcome unknown | Retry upload-complete for the same document. | Do not upload the file again and do not create a replacement document. | Processing may already be queued or finished. |
| SDK cancelled or stopped waiting | The SDK stops waiting, but server-side work is not cancelled: processing continues. | Resume with an authenticated GET using the document ID, or recover it with the same idempotency key. | The document may still finish, and is billed normally if it does. |
Result expired or document not found
| No. Do not treat either response as a successful DELETE or a completed result. Code-specific guidance
| Check the document ID and the account. Save results before result_expires_at; an expired result cannot be recovered. A 404 is final. | No result was returned, and no new document was created. |
Usage period not available
| Choose a closed period that still exists, or omit periods_ago for current usage. Code-specific guidance
| This is a usage-query error; do not resubmit or delete an invoice. | The period does not exist for this account. It does not mean a document was deleted or that usage was zero. |
Processing failed or timed out
| No. The document has reached its final failed status, and polling does not restart it. Check retryable, and contact support with the document ID if needed. Code-specific guidance
| Do not automatically change the key or upload again. To try again, submit a new document on purpose, with a new idempotency key. | No usable result, and genuine processing failures are not billed. processing_timeout is a server-side time limit, different from the SDK giving up on polling. |
Page, complexity, or result-size limit
| Not with the same file. Check the limit and send a supported version of the complete invoice, or ask for help. Code-specific guidance
| The failed document is final. Do not split one invoice into parts or remove content to fit a limit. A suitable replacement is a new submission. | No usable result; limit failures are not billed. Overage or a larger plan does not raise these limits. |
Rejected fields
When the API rejects request input, error.details.issues lists each rejected input, at most 20. Fix every listed input before you send the request again.
locationstring
Where the input is: body, query, header, or path.
pathstring
A JSON Pointer to the input, such as /policy/line_items.
codestring
A stable code to branch on. See the request issue codes.
messagestring
An explanation for people. Never branch on it.
Issue code | Meaning |
|---|---|
required | A required field or value is missing. |
invalid_type | The value has the wrong JSON type. |
invalid_value | The value is not one of the allowed values. |
invalid_format | The value does not have the required format. |
too_large | The value is longer, larger or has more items than allowed. |
too_small | The value is shorter, smaller or has fewer items than allowed. |
unknown_field | The field is not accepted here. |
malformed | The body or path could not be read, such as JSON that does not parse. |
invalid | The value breaks another rule, which |
Codes you do not recognize
New codes may be added over time. Keep an unknown code for logging and display, then fall back on fields you already handle. For an API error, use the HTTP status and retryable. For a check, use its status, severity, paths and validation.review_required. A document that failed with an unknown code is still failed for good.
SDK errors versus failed documents
The SDK’s wait and process return a failed document normally; they do not throw for it. They throw when you cancel, when polling times out, when a response is invalid, or when the API returns an error. A polling timeout does not stop processing on the server: resume with a GET using the document ID.
Callbacks
A callback tells you a document has finished. It is a notification, not the result: always fetch the document to see what happened.
- Receive
document.finished. - Read its event ID and document ID. Use them only to decide which document to fetch.
- Fetch that document with an authenticated GET.
- Check that the fetched
callback.event_idmatches the event ID you received, then act on the fetched document.
Callbacks are not signed, and you set the callback URL on each document; there is no webhook endpoint to register. Fetch the document promptly and store only what your workflow needs.
Keep secrets and personal data out of callback URLs, because the platform logs of Vercel, our hosting provider, may retain them. Workflow resume URLs, such as n8n’s, can be secrets themselves; poll instead if that is a problem. See the retention limits.
Callback body
idstring
The event's ID. The fetched document's callback.event_id matches it.
created_atdate-time
When the event was created.
dataobject
Which document finished.
3 child fields
document_idstring
The document to fetch.
accounting_entity_idstring · nullable
Your accounting entity from create document, or null.
- Length
- 1–255 characters
Callback delivery errors
When a delivery attempt fails, the document’s callback.last_error_code says why. Temlavo retries temporary failures, making up to 8 attempts within 24 hours of the event, and keeps the delivery pending meanwhile; callback.next_attempt_at shows the next one. A permanent failure, or running out of attempts or time, makes it failed. After a 429 or 503, a reasonable Retry-After sets the minimum wait, but never extends the window. A failed callback never changes the document: fetch it whenever you need its result.
last_error_code | Meaning | Temlavo retries |
|---|---|---|
dns_error | The callback hostname could not be resolved. Fix: Publish public DNS records for the callback hostname. | Yes |
unsafe_destination | The callback URL is not a valid destination, or its hostname resolves to no address or to a loopback, private, reserved or other non-public address. Fix: Use a hostname whose DNS answers are all public addresses, and give later documents the corrected | No |
connection_error | The connection or TLS handshake failed or was reset, or the response could not be read, for example an invalid status line. Fix: Accept HTTPS connections on port 443 and answer with a valid HTTP response. | Yes |
timeout | No response status arrived within the attempt’s time budget, which covers DNS, connection, TLS and the response. Fix: Answer with a 2xx as soon as the event arrives, and do slow work afterwards. | Yes |
tls_error | The endpoint’s certificate failed verification, for example because it is self-signed, expired or not yet valid, missing intermediate certificates, or issued for another hostname. Fix: Serve a currently valid certificate for the callback hostname from a publicly trusted authority, with its full intermediate chain. | No |
http_error | The endpoint answered with a status other than 2xx, which | For 408, 425, 429, 5xx |
response_too_large | The endpoint’s response headers exceeded the size limit. Fix: Keep response headers small; delivery reads only the status and | Yes |
Result retention and deletion
Temlavo is not a long-term invoice archive. Results are kept for 7 days by default, and processing.result_expires_at gives the exact deadline. After it, retrieving the document returns 410 document_expired; that is expected, not a fault.
DELETE ends API access to a document and clears its stored result and fields. Storage deletion is attempted immediately and retried if needed. Some records remain after a document is deleted: your account’s discovery index of accounting-entity IDs (the accounting_entity_id values you have used, kept while the account is open), required financial and security records, and the platform logs of Vercel, our hosting provider. Deleting a document does not close your account. See the Privacy Notice's retention and account-closure details.
Your own database, callback handler, CLI output, CI logs, and n8n execution history are outside Temlavo retention and deletion control.
Support and debugging
When you ask for help, include the X-Request-Id, document ID and idempotency key. Do not send your API key, a signed upload URL, the invoice file, the full result, or provider details in your first message.
Contact support with the safe identifiers above and a short description of the issue.