Skip to navigation

Verify Error Handling

Beta

When an error occurs, the Verify API returns a structured error with the following fields:

FieldTypeDescription
codestringA specific error code for programmatic handling.
messagestringA human-readable description of the error. Don’t rely on its exact text — it may change.
retryablebooleanWhether retrying the request might succeed. When true, retry with exponential backoff.
requestIdstringA unique identifier for the request, useful when contacting support.
runIdstringThe ID of the verify run that is still processing. Returned only when a synchronous /verify request times out.

How failures surface: sync vs. async

Where an error shows up depends on which endpoint you use.

  • Synchronous (/verify) waits for processing to finish, so every failure — both request validation and document processing — comes back as an HTTP error response with the fields above. The HTTP status codes below apply to this endpoint. The wait is capped at 5 minutes on both engines. If the run outlives it, the response is usually a 504 Gateway Timeout from the load balancer, which has no Extend error body and no runId. Less often it is a 500 INTERNAL_ERROR with retryable: false whose body carries runId. In both cases the run keeps processing, and resubmitting starts a second run. verify_agentic runs often exceed 5 minutes, so use the asynchronous endpoint for them.
  • Asynchronous (/verify_runs) returns 200 with a verify run as soon as the job is accepted. Request-level problems (invalid request, auth, a file URL that cannot be downloaded) still return an HTTP error at creation time, but document processing failures are not HTTP errors — the run instead reaches status: "FAILED" with a failureReason and failureMessage (one of the failure reasons below), which you read when you poll.

Production integrations should use the asynchronous /verify_runs endpoint and check status on the returned run. Webhook events for verify runs are not available yet, so poll GET /verify_runs/{id} until the status is PROCESSED, FAILED, or CANCELLED.

Failure reasons

These codes describe why a verify run failed. On the sync endpoint they appear in the error body’s code field; on the async endpoint they appear as the run’s failureReason. The structured error’s retryable flag is authoritative at runtime.

Failure reasonDescriptionRetryable
FILE_TYPE_NOT_SUPPORTEDThe file is not a PDF, JPEG, PNG, or WebP. See supported file types.❌
NO_APPLICABLE_SIGNALSNo verification check could run against this document, for example an image on which none of the configured detection services are available.❌
PARSE_FAILEDThe document could not be parsed, so the checks that read the parse could not run. Applies to verify_agentic and to verify_light with advancedOptions.requireParsing.✅
REQUEST_ABORTEDThe client disconnected before the synchronous request completed.❌
INTERNAL_ERRORAn internal error stopped the run, such as a detection model, content analysis, or the assessment being unavailable. Retry with exponential backoff. Also returned when the synchronous request times out; that case is not retryable because the run is still processing.✅ (❌ on a sync timeout)

Additional failure reasons may be added in the future. Handle unknown values gracefully.

Transient failures on the async endpoint are retried automatically before the run is marked FAILED, so a FAILED run with a retryable reason has already been attempted more than once. Retrying by creating a new run is still reasonable after a short delay.

HTTP status codes

These HTTP status codes are returned by the synchronous /verify endpoint. On the asynchronous /verify_runs endpoint, only request-level errors are returned at creation time — processing failures surface on the run as status: "FAILED".

400 Bad Request

Returned when required fields are missing (for example file), the file locator is invalid or points at a private network, the file at url could not be downloaded, config.engineVersion is unsupported, config contains a key that is not recognised (including the removed signalAllowlist and policy), advancedOptions.requireParsing is false on verify_agentic, metadata exceeds 10 KB, or a list filter or pagination token is invalid. The code is INVALID_REQUEST or the name of the failed validation.

401 Unauthorized

Returned when the API token is missing or invalid.

402 Payment Required

Returned when the organization is blocked from creating runs because of an overdue balance or exhausted credits (USAGE_BLOCKED).

403 Forbidden

Returned when the API key does not have permission to create or read document-processing runs.

404 Not Found

Returned when the run ID does not exist in this workspace or has been deleted, when a referenced file has been purged (FILE_PURGED), and when Verify is not enabled for your organization. During the beta, every verify endpoint returns 404 NOT_FOUND for organizations that have not been granted access; contact support@extend.ai to request it.

422 Unprocessable Entity

Returned when the file cannot be verified: it is not a supported type (FILE_TYPE_NOT_SUPPORTED), or no check applies to it (NO_APPLICABLE_SIGNALS).

429 Too Many Requests

Returned when you exceed the rate limit. Retry with exponential backoff. See Rate Limits.

500 Internal Server Error

Returned as INTERNAL_ERROR when an internal error stops the run (for example, a detection model, content analysis, or the assessment is unavailable) or the synchronous request exceeds its 5-minute timeout, and as PARSE_FAILED when the document could not be parsed. Retry with exponential backoff when retryable is true. A timeout returns retryable: false: the run continues in the background and the error body includes its runId, so fetch that run instead of resubmitting the file.

504 Gateway Timeout

Returned by the load balancer when a synchronous /verify request runs past 5 minutes. The response has no Extend error body and no runId. The run continues in the background, so don’t resubmit the file. Use the asynchronous /verify_runs endpoint for verify_agentic and for long documents.

Handling errors

The async pattern below creates a run, polls it to a terminal state, and branches on the outcome.

import time
from extend_ai import Extend
from extend_ai.core.api_error import ApiError
client = Extend()
try:
run = client.verify_runs.create(file={"url": "https://example.com/statement.pdf"})
except ApiError as e:
# Request-level failure: bad input, auth, rate limit, or Verify not enabled (404)
print(f"Request failed ({e.status_code}): {e.body}")
raise
while run.status == "PROCESSING":
time.sleep(2)
run = client.verify_runs.retrieve(run.id)
if run.status == "PROCESSED":
print(run.output.risk_level, run.output.summary)
elif run.status == "FAILED":
if run.failure_reason in ("PARSE_FAILED", "INTERNAL_ERROR"):
print("Transient failure, retry later:", run.failure_message)
else:
print("Not retryable:", run.failure_reason, run.failure_message)
else:
print("Run was cancelled")

Cancelling a run

POST /verify_runs/{id}/cancel moves a PROCESSING run to CANCELLED and stops in-flight work. Cancelling a run that has already finished, failed, or been cancelled is a no-op that returns the run unchanged, so it is safe to call from a timeout handler without checking status first. A cancelled run never has output.

Deleting a run

DELETE /verify_runs/{id} removes a run and everything derived from its document: the result, the evidence artifacts, and the embedded parse. A run that is still processing is cancelled first. The input file is never deleted, even a zero-retention file another run is using. After deletion the run returns 404 from every read and no longer appears in GET /verify_runs. The response is { "id": "vr_..." }.

For the general error format and codes shared by every endpoint, see Error Handling.