New France e-invoicing mandate goes live September 2026 — our implementation is ready. See all mandates →
Error Reference

Errors & status codes

Every Clearvo product returns the same HTTP status codes and a consistent JSON error envelope. Product-specific rejection reasons — e.g. e-invoicing authority clearance codes — are documented on each product's own page and linked from here.

Error Reference

HTTP errors

Clearvo uses standard HTTP status codes. Error responses include a JSON body (usually with ok: false).

StatusMeaningCommon cause
401 Unauthorized Missing or invalid x-api-key. Check the header name (lowercase) and that the key is active.
400 Bad Request The request cannot be read: invalid JSON, a missing required field or an invalid value. The response includes field and message for each violation. On POST /send this is only a malformed request (unparseable JSON, no country or invoiceNumber, a bad x-idempotency-key, an unrecognised country code, or a platform-set field such as channel or source). A well-formed document that fails a validation check is stored and answered 200 instead, see below.
403 Forbidden The key is valid but not authorized for this entity or product — e.g. an entity-scoped key used against a different entity, or a read-only key on a write operation. Purchase-side tax calculation is the Enterprise-only Purchases (AP) sub-solution: ap_not_included (plan does not include it) or ap_not_enabled (switch Purchases (AP) on under Settings > Solutions).
404 Not Found No record exists for the id you passed — a wrong referenceId on GET /status, an invoice id that belongs to another entity, or a resource that was deleted. Check the identifier.
409 Conflict Conflict. On POST /send, most often IDEMPOTENT_BODY_MISMATCH — you re-sent an already processed document with different content; the stored document is not changed and the refused edit is recorded in its history, so send the change as a correction instead. (Reusing an x-idempotency-key for a different document is no longer an error: the document is processed under its own identity and the response carries the warning IDEMPOTENCY_KEY_REUSED_ACROSS_DOCUMENTS.) Other conflicts carry their own code, for example DUPLICATE_INVOICE_NUMBER (the invoice number already exists for this entity; invoices, credit notes and debit notes share one number space) and ALREADY_CORRECTED (the invoice already has an active or successful correction).
422 Unprocessable Entity A well-formed request whose values fail validation on an endpoint that does not store a document (for example POST /tax/calculate or the configuration endpoints). POST /send does not answer 422: a missing customer taxId for reverse charge or an invalid Italian Partita IVA check digit is stored as a rejection and answered 200 with the error in errors[].
413 Payload Too Large An uploaded file or batch is over the limit — for example a document sent to POST /receive (FILE_TOO_LARGE), too many files in POST /receive/bulk (TOO_MANY_FILES), or an oversized POST /send/bulk or POST /mx/inbound/cfdi upload. Split the upload.
415 Unsupported Media Type The uploaded file is not a supported document type (POST /receive). Send a supported XML or PDF file.
429 Too Many Requests This API key has used up its per-minute budget for the endpoint class, or a bulk upload does not fit the remaining rows budget. Nothing was processed. Wait Retry-After seconds and retry — see Rate limits.
500 Internal Server Error Unexpected Clearvo error. The response includes a requestId — include this when contacting support.
503 Service Unavailable On POST /send, INGEST_UNAVAILABLE means the document could not be stored at all. Nothing was recorded: retry with the same body and the same x-idempotency-key, after the Retry-After seconds.

An errors[] entry with UNKNOWN_FIELD_BUYER_RENAMED (POST /send body, stored as a rejection with HTTP 200) or UNKNOWN_HEADER_BUYER_RENAMED (bulk CSV header row) means the request still carries a retired buyer* input — see E-Invoicing for the current customer* field names.

ℹ
POST /send answers 200 for every document it stores. A validation failure, a document that needs information, a hold, a document not supported yet, and one an authority later rejects all come back as 200 with the outcome in the body: read status, reason and nextStep, and the error and errors[] on a rejection. status is one of ten values (RECEIVED means stored and not processed yet: poll GET /v1/status or wait for the invoice.status_changed webhook). An unroutable Peppol customer is WAITING_FOR_YOU with reason: UNROUTABLE. outcome is derived from status. ok comes from the reason alone: it is false for REJECTED, NEEDS_INFO, SETUP_NEEDED and HELD_PLATFORM_FAULT, true otherwise, and the same on every replay of the document (a replay carries the same error, errors[], fixUrl and nextStep). When enabled for your account, a temporary problem after the document is stored gives 200 with status: RECEIVED and a PROCESSING_DELAYED warning: do not resend it. Otherwise a temporary failure is a 503 with Retry-After: retry the same request with the same x-idempotency-key. See Clearance errors.
Error Reference

Rate limits

Requests are counted against the API key that made them, in fixed 60-second windows, however many IP addresses the key is used from. Each endpoint class has its own budget, and sandbox and production keys are counted separately. The limits sit well above normal integration traffic — they exist to keep one runaway client from slowing everyone else down.

Endpoint classEndpointsLimit per key
Bulk submissionPOST /send/bulk, POST /receive/bulk30 requests/min
Single submissionPOST /send, POST /receive120 requests/min
Tax CalculationsPOST /tax/calculate, POST /tax/calculate/{id}/refund, POST /integrations/coupa/tax-callout600 requests/min
Heavy operationsGET /pt/saft, POST /invoices/resend-notification/batch, POST /mx/inbound/cfdi, POST /mx/inbound/poll60 requests/min
Everything elseEvery other authenticated endpoint — reads, status polls, entity and registration management, webhook configuration300 requests/min

POST /query and GET /export have their own per-key budgets, documented on those endpoints, and are not counted above. When they are exceeded they return RATE_LIMITED (HTTP 429) rather than the RATE_LIMIT_EXCEEDED code used by the per-key limits in the table above. GET /health is unauthenticated and not counted.

Bulk rows budget

On top of the request count, bulk uploads are budgeted by size. The synchronous POST /send/bulk transport (uploads under 25MB) charges each CSV data row, and POST /receive/bulk charges each document, against 20,000 rows per minute per key. An upload larger than what remains of that budget in the current window is rejected whole with a 429 — nothing in it is processed — so split the file or wait for the window to reset. Uploads of 25MB or more to POST /send/bulk (up to 200MB) are stored and processed asynchronously and are not row-budgeted.

Response headers

Every counted response, success or error, tells you where you stand:

HeaderMeaning
X-RateLimit-LimitRequests allowed per minute for this key on this endpoint class.
X-RateLimit-RemainingRequests left in the current window.
X-RateLimit-ResetUnix epoch seconds at which the window resets.
X-RateLimit-Scopeapi-key — the budget the headers above describe.
X-RateLimit-Rows-Limit, X-RateLimit-Rows-Remaining, X-RateLimit-Rows-ResetThe bulk rows budget — on POST /send/bulk (synchronous) and POST /receive/bulk only.
Retry-AfterOn a 429 only: seconds to wait before retrying.

Handling a 429

A rate-limited request is not processed. Wait Retry-After seconds, then retry. If you send in bursts, spread the calls across the minute, or use the bulk endpoints instead of many single submits. Every rate-limited response has the same body:

JSON — Rate limited (429)
{
  "ok": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded for this API key: 120 requests per minute on this endpoint class. Retry after 37 seconds.",
    "retryable": true
  },
  "hint": "You've exceeded the rate limit for this API key. Spread requests out, honour Retry-After, or contact support@clearvo.io for higher limits."
}

A separate network-level protection against abusive traffic also exists, set well above the per-key limits. It returns the same body with X-RateLimit-Scope: ip; a legitimate integration will not encounter it. If your integration genuinely needs higher limits, contact support@clearvo.io.

Error Reference

Error object

Most error responses include ok: false and an error object with code and message, plus an optional hint. Two envelope shapes exist, so read defensively:

  • { "ok": false, "error": { "code", "message", "retryable" }, "hint" } — the shape described below.
  • { "error": "message string", "hint": "…" } — a bare string in error, used by several endpoints (for example GET /invoices, the upload endpoints and the invoice action endpoints). Some of these add a sibling code field (for example "code": "DUPLICATE_INVOICE_NUMBER" or "FILE_TOO_LARGE"); POST /send can return either shape depending on the failure.

If error is a string, use the top-level code when present, otherwise the HTTP status.

FieldTypeDescription
okbooleanAlways false on an error response.
error.codestringStable, machine-readable identifier — branch on this, never on message.
error.messagestringHuman-readable explanation.
error.retryableboolean, optionalPresent only when meaningfully true or false — e.g. true on a 429.
hintstring, optionalOne plain-language sentence on what to do about it.

A specific endpoint may add its own fields on top of this base envelope — e.g. violations[] on a structural validation failure, or source/detail on an authority clearance rejection (both shown below).

JSON — HTTP error (400)
{
  "ok": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Request body validation failed",
    "violations": [
      { "field": "lines[0].taxRate", "message": "taxRate is required and must be a number between 0 and 100" }
    ]
  }
}
JSON — Clearance rejection (from status poll)
{
  "status": "REJECTED",
  "reason": "REJECTED",
  "terminal": true,
  "rejectedAt": "2024-01-15T10:47:12Z",
  "error": {
    "source": "SDI",
    "code": "00102",
    "message": "Partita IVA fornitore non trovata",
    "detail": "Supplier tax ID 01234567890 is not registered with the Italian Revenue Agency"
  }
}
JSON — Internal error (500)
{
  "ok": false,
  "error": {
    "code": "INTERNAL_ERROR",
    "message": "An unexpected error occurred",
    "requestId": "req_01J9XKZ4MFBVQH7PRTD8CNWY5"
  }
}