NF-e verification
XML verification
Submits the raw NF-e XML; returns the Tier-1 verdict and the parsed invoice and starts the SEFAZ verification.
/openapi/v3/consultas/nf-e/xmltoken, timestamp and sign signature headers, see Authentication.Submits the raw NF-e XML. Returns the Tier-1 verdict and the parsed invoice data synchronously; on Tier-1 pass, Tier-2 SEFAZ verification starts automatically and the final verdict arrives through the invoice.verify.completed webhook (see NF-e verification).
HTTP 200 means the request was accepted, not that the invoice passed. A well-formed XML that fails validation still returns 200; the verdict is in the validation block. Never branch on the HTTP status to decide validity.
Headers
Content-Type* | string | Body type |
forceRevalidate | bool | Forced re-verification |
* Required field
validation block with the Tier-1 verdict. Includes failed validation: check validation.validationStatus and validation.errors.tipo | string | Document type |
modelo | string | Document model |
status | string | Fiscal status |
statusDescription | string | Status description |
ambienteEmissao | string | Issue environment |
numero | string | Invoice number |
serie | string | Series |
dataEmissao | string | Issue time |
tipoOperacao | string | Operation type (tpNF), the direction of goods as seen by the issuer |
chaveAcesso | string | Access key |
| object | Issuer |
| object | Recipient |
| array | Items |
dataAutorizacao | string | Authorization time |
| object | Authorization protocol |
valorTotal | number | Invoice net total |
| object | Tier-1 verdict |
- Preferred: the full document authorized by SEFAZ, with the authorization protocol. An invoice without the protocol is also accepted: it produces a
PROTOCOL_MISSINGwarning but is processed normally. - Hard limits: max 1 MB, UTF-8 only, DTD forbidden (any
<!DOCTYPEis rejected outright). - Signing: the body participates in the signature after stripping every CR and LF, while the request body itself is sent unchanged:
sign = md5Hex(app_secret + "/openapi/v3/consultas/nf-e/xml" + xmlWithoutCrLf + timestamp)
The forceRevalidate header is not part of the signature. Full rules in Authentication.
Line-level itens[].valorTotal is the gross line amount, before discount; root-level valorTotal is the net invoice total, after discount. The difference equals the total discount.
Amounts and quantities are JSON numbers taken from the document with trailing zeros removed: 100.00 is returned as 100 and 94.90 as 94.9. Format them for display. Fields the document has no value for are omitted, never returned as null.
Tier 1 is decided immediately and returned in this response; Tier 2 is the SEFAZ verification and arrives only through the webhook. The values of validationStatus and errors[].code are in the enum tables at the bottom of the page.
Tier 1 rejected in the synchronous response
HTTP 200 with validationStatus = REJECTED and at least one entry in errors. It is terminal: there is no Tier 2 and re-verification requires resubmitting the XML (with forceRevalidate: true inside the 24-hour window).
"validation": {"fileAccessible": true,"xmlWellFormed": true,"schemaValid": true,"signatureValid": false,"accessKeyValid": true,"protocolPresent": true,"protocolValid": true,"xmlVersion": "4.00","validationStatus": "REJECTED","validationStatusDescription": "Verification failed","errors": [ { "code": "SIGNATURE_INVALID", "field": "Signature", "message": "Digital signature verification failed: ..." } ],"warnings": []}
Tier 2 through the webhook
Once Tier 1 passes (PENDING_SEFAZ), the final verdict arrives through the invoice.verify.completed webhook with VALIDATED, REJECTED or VALIDATION_ERROR; headers, payload and receiver requirements are in NF-e verification. If a delivery is missed, the chave lookup returns the current validationStatus. These are not HTTP errors.
- XML verification is idempotent on the chave: while Tier 2 is in progress, resubmitting returns the current progress and starts no other check;
forceRevalidate: truedoes not restart or speed it up either. - Terminal verdicts are reused for 24 hours from the moment they were reached,
VALIDATION_ERRORincluded; after that a plain resubmission runs the full verification again. - A reused response reflects the originally submitted XML with the current
validationStatus; to verify a corrected XML for the same chave within the window, sendforceRevalidate: true. - Forced re-verification: send the header
forceRevalidate: true; the new verdict arrives as a new webhook event. The chave lookup cannot force a re-check. - A Tier-1 blocking
REJECTEDhas no Tier-2 record; re-verification requires resubmitting the XML. - Verifications belong to your integrator account: another integrator verifying the same invoice does not affect yours, and each account gets its own verdict.
Content-TypeBody type (header)
| Values | Meaning |
|---|---|
| application/xml | XML body |
| text/xml | XML body; equivalent |
forceRevalidateForced re-verification (header)
Omitted means false.
| Values | Meaning |
|---|---|
| true | Verifies again from scratch, ignoring a reusable terminal verdict |
| false | Reuses the terminal verdict of the last 24 hours, if any |
tipoDocument type
| Values | Meaning |
|---|---|
| NF-e | Electronic invoice used between companies and on delivered sales |
| NFC-e | Electronic consumer invoice, over-the-counter sales |
modeloDocument model
| Values | Meaning |
|---|---|
| 55 | NF-e |
| 65 | NFC-e |
statusFiscal status
| Values | Meaning |
|---|---|
| Autorizada | Authorized by SEFAZ with no cancellation event |
| Cancelada | Cancelled, including by substitute cancellation |
| Denegada | Denied by SEFAZ |
| Inutilizada | Invoice number voided |
| NaoEncontrada | Not found at SEFAZ; may be a synchronization delay |
| Desconhecida | Status not classifiable from the SEFAZ reply |
ambienteEmissaoIssue environment
| Values | Meaning |
|---|---|
| Producao | Official environment |
| Homologacao | SEFAZ test environment; the invoice has no legal effect |
tipoOperacaoOperation type
| Values | Meaning |
|---|---|
| Saida | Goods leaving the issuer (tpNF=1), such as a sale |
| Entrada | Goods entering the issuer (tpNF=0), such as a consumer return or an import |
validation.validationStatusVerification status
The first synchronous response carries only PENDING_SEFAZ or REJECTED; the others arrive through the invoice.verify.completed webhook and also show up in reused responses and the chave lookup.
| Values | Meaning |
|---|---|
| PENDING_SEFAZ | Tier 1 passed, SEFAZ verification queued |
| REJECTED | Rejected; terminal. At Tier 1 by a blocking error, at Tier 2 by the SEFAZ status (cancelled, denied, voided, not found, protocol mismatch); do not proceed |
| VALIDATING | Tier 2 in progress, querying SEFAZ |
| VALIDATED | Invoice authentic and regular at SEFAZ; terminal, safe to proceed |
| VALIDATION_ERROR | Platform-side technical failure, not a judgment on the invoice; reused for 24 hours like any verdict, resubmit later with forceRevalidate: true |
validation.errors[].codeTier-1 issue codes
All are blocking and lead to REJECTED, except PROTOCOL_MISSING, which goes to warnings[]. The number in parentheses is the equivalent numeric code.
| Values | Meaning |
|---|---|
| XML_MALFORMED | XML syntax invalid (10015100) |
| XSD_INVALID | Does not conform to the official NF-e 4.00 layout (10015101) |
| SIGNATURE_INVALID | Digital signature invalid: content tampered or certificate expired at signing time (10015102) |
| SIGNATURE_CERT_MISMATCH | Signing certificate CNPJ differs from the issuer (10015103) |
| ACCESS_KEY_INVALID | Access key structure or check digit invalid (10015104) |
| ACCESS_KEY_MISMATCH | Access key segments differ from the document fields (10015105) |
| PROTOCOL_MISMATCH | Authorization protocol inconsistent with the invoice (10015106) |
| PROTOCOL_MISSING | No authorization protocol; warning, non-blocking (10015107) |
| XML_VERSION_UNSUPPORTED | Layout version is not 4.00 (10015108) |
| Code | HTTP | |
|---|---|---|
| 10015000 | 400 | Empty request body. Send the XML in the body. |
| 10015001 | 400 | Body exceeds 1 MB. Check that you are not wrapping or double-encoding. |
| 10015002 | 400 | DTD detected (<!DOCTYPE). Strip DTDs. |
| 10015003 | 400 | Encoding is not UTF-8. Convert to UTF-8 before submitting. |
