General Conventions
Conventions shared by the endpoints of the TF Fiscal Open API.
Response shapes
The API has two response shapes, and which one you receive depends on the endpoint and on which layer produced the response.
Platform envelope
Used by the echo endpoint (POST /openapi/demo/echo) for every response, and by the
platform gateway for authentication-layer errors on every endpoint (HTTP 401 / 403 /
410 / 429), which are produced before the request reaches the endpoint:
// echo success
{ "success": true, "message": "OK", "data": { } }
// gateway error (any endpoint)
{ "success": false, "errorType": 1, "code": 10009003, "message": "Signature error" }| Field | Type | Presence | Description |
|---|---|---|---|
success | boolean | always | false for every error |
errorType | integer | on error | Error class — see Error typing |
code | integer | on error | Platform error code — see Error Codes |
message | string | always | Human-readable explanation, localized |
data | object | null | on success | Endpoint payload; not populated on errors |
Bare responses
All standard endpoints (issuance, verification, identity) respond without the envelope:
| Endpoint group | Success shape |
|---|---|
| Company registration, webhook registration | Bare object ({ "empresaId": ... }, { "webHookId": ... }) |
| Certificate association, NF-e issuance, NF-e cancellation | HTTP 200 with no body |
| NF-e query | Bare object: the invoice itself |
| XML verification, chave lookup | Bare object: the parsed invoice (XML verification adds a validation block) |
| CNPJ lookup, CPF lookup | Bare object: the registry record |
Business and request-level errors of the standard endpoints come in two error shapes, by endpoint family:
Issuance family (HTTP 400 / 404) — an array of errors, each with codigo and
mensagem. Request-body validation failures produce one entry per field:
[
{ "codigo": "NFe0001", "mensagem": "A Nota fiscal nao foi encontrada. Por favor, verifique se o id foi informado corretamente" }
]codigo is a string: the three codes GW001 (invalid city / state), CER0005
(certificate password mismatch) and NFe0001 (invoice not found) keep their alphanumeric
values; all other entries carry the platform error code as a numeric string.
Verification and identity families (HTTP 400) — a bare object with code and
message:
{ "code": 10015004, "message": "Invoice not found" }| Field | Type | Description |
|---|---|---|
code | integer | Platform error code |
message | string | Human-readable explanation, localized |
Handling both shapes
Your error handler must accept the envelope and the bare error shapes. A robust rule:
- Treat HTTP ≥ 400 as failure, and additionally
success: falsewhen the body is an envelope. - Parse the body as JSON. If it is an array, read
codigofrom the first entry; if it is an object, readcode. - Branch on the code, never on
message/mensagemtext (they are localized).
Error typing (errorType)
Envelope errors carry an errorType field that classifies the failure source:
| errorType | Class | Meaning |
|---|---|---|
| 1 | API error | Authentication, authorization or business rejection by TF Fiscal |
| 2 | SEFAZ rejection | The Brazilian tax authority rejected the operation |
| 3 | System fault | Unexpected platform failure; safe to retry with backoff |
| 4 | Validation failure | Request field validation failed |
HTTP status quick reference
| HTTP | Scenario | Body shape |
|---|---|---|
| 200 | Request accepted — for XML verification this includes a failed validation (check the validation block) | Bare data, or no body |
| 400 | Request invalid or business rule violated | [{codigo, mensagem}] (issuance) or {code, message} (verification / identity) |
| 404 | empresaId / nfeId not found (issuance family) | [{codigo, mensagem}] |
| 401 | Authentication failed (token / sign / timestamp) | Platform envelope |
| 403 | Application disabled / not effective / integrator disabled / not subscribed | Platform envelope |
| 410 | Endpoint retired — see Deprecated Endpoints | Platform envelope |
| 429 | Rate limit exceeded | Platform envelope |
| 5xx | Platform-side fault | Retry with backoff; if persistent, contact the platform with the failing timestamp and path |
Idempotency
- NF-e issuance is idempotent on the request
idyou generate: re-submitting the sameidwithin the idempotency window reuses the original task instead of creating a new one. Use a stable, uniqueidper invoice and retry with the same value. A denied invoice (Negada) is resubmitted under a newidafter fixing the cause. - XML verification is idempotent on the access key (
chave): resubmitting while a verification is in progress returns the current progress, and terminal verdicts are reused for 24 hours. To force a re-check, send the headerforceRevalidate: trueon the XML endpoint (the header is not part of the signature). The chave lookup is a fixed GET and has no re-verification channel. A Tier-1REJECTEDhas no Tier-2 record; re-verification requires resubmitting the XML. - Webhook deliveries are idempotent by
event_id— see Webhooks.
Rate limiting and anti-flood
- Per-application quota: exceeding it returns HTTP 429 with code 10009006. Limits are enforced per application and per endpoint group. Back off and retry (start at 1 s, double up to 30 s, add jitter) and smooth your call rate.
- Per-CNPJ anti-flood: if a company accumulates too many pending issuance tasks, new
submissions are rejected with
codigo10004002 until the queue drains — retry later. - Timestamp window: requests with a
timestampbeyond ±300 s of the server clock are rejected (10009001), which also bounds replay of captured requests. - A 410 response for a retired endpoint consumes no rate-limit token.
Response localization
message, mensagem and *Description fields follow the request language: the
Language header (en / pt / es / zh) takes precedence, then Accept-Language
(supports pt-BR and q-values). Without a language header, responses default to
Portuguese (pt).
Webhook payloads carry only language-independent enum values — there are no
*Description fields there. For programmatic decisions always use codes and enum fields
(code, codigo, status, validationStatus, errors[].code, situacao.codigo),
never description text.
Timestamps and data types
- Times produced by the platform (issuance API
dataCriacao,dataAutorizacao, webhookoccurred_at,verifiedAt, …) are ISO-8601 UTC with theZsuffix. - Times parsed from a third-party document by the verification API (
dataEmissao,dataAutorizacao) are returned verbatim with the document’s UTC offset, e.g.2026-07-23T11:20:05-03:00. - The
timestamprequest header is Unix time in seconds, not milliseconds. - Identifiers are strings even when numeric (
empresaId,webHookId,event_id), to avoid JavaScript number precision loss. Treat all ids as opaque strings. - Invoice numbers (
numero) and series (serie) are strings. Amounts are JSON numbers. - Download links (
linkDanfe,linkDownloadXml,nfeLinkXml) embed a 10-minute token; fetch a fresh link each time you need the file rather than persisting the URL.
The NF-e access key (chave)
The chave de acesso is the nationally unique, 44-digit identifier of an NF-e. It appears
as chaveAcesso in query and verification responses and as the path variable of the chave
lookup. Its structure:
| Positions | Length | Field | Meaning |
|---|---|---|---|
| 1–2 | 2 | cUF | IBGE code of the issuing state (e.g. 35 = SP) |
| 3–6 | 4 | AAMM | Year and month of issue (YYMM) |
| 7–20 | 14 | CNPJ | Issuer CNPJ |
| 21–22 | 2 | mod | Fiscal document model (55 = NF-e) |
| 23–25 | 3 | serie | Invoice series |
| 26–34 | 9 | nNF | Invoice number |
| 35 | 1 | tpEmis | Emission type |
| 36–43 | 8 | cNF | Random numeric code |
| 44 | 1 | cDV | Check digit (modulo 11) |
Notes:
- Always store and transmit the chave as a 44-character string (leading zeros are significant).
- The chave lookup validates length, characters and the check digit locally before any lookup is attempted; a malformed chave returns HTTP 400 with code 10015104.