Issuance API
Issue NF-e documents on behalf of sellers. The domain exposes six endpoints, used in the order below:
| Step | Endpoint | Notes |
|---|---|---|
| 1 | POST /openapi/v2/empresas | Register the seller’s company; returns empresaId, the path variable of every later call |
| 2 | POST /openapi/v1/empresas/{empresaId}/certificadoDigital | Upload the seller’s A1 digital certificate (.pfx) and its password |
| 3 | POST /openapi/v1/webhooks | Register the callback URL for issuance results (authorized / denied) |
| 4 | POST /openapi/v2/empresas/{empresaId}/nf-e | Accepted immediately; authorized asynchronously with SEFAZ |
| 5 | GET /openapi/v2/empresas/{empresaId}/nf-e/{nfeId} | Status, invoice data, DANFE / XML download links |
| 6 | DELETE /openapi/v2/empresas/{empresaId}/nf-e/{nfeId} | Within 24 hours of authorization |
All endpoints require the three signature headers — see
Authentication & Signing. GET, DELETE and multipart requests
sign the empty string as body; path variables are part of the signed path. Responses
are bare (no platform envelope) and business errors come as an array of
{codigo, mensagem} — see General Conventions.
Company status and prerequisites
- Registration places the company in the platform’s approval queue. The company can
issue only after operations approve it and the certificate is linked. Issuing
before that returns
codigo10004004 (company not issuable). - Every company has one current environment: test (
Homologacao) or production (Producao). Newly registered companies default to test; switching to production is an operations action. TheambienteEmissaoof an issuance request must match the company’s current environment, otherwise the request is rejected with 10004030 — a hard guard against test invoices being issued into production. See Environments.
Company registration
POST /openapi/v2/empresas
Content-Type: application/jsonRequest
{
"cnpj": "14422279000106",
"inscricaoMunicipal": "999999",
"inscricaoEstadual": "999999",
"razaoSocial": "Empresa teste LTDA",
"nomeFantasia": "Empresa teste",
"optanteSimplesNacional": true,
"mei": false,
"email": "empresa-teste@example.com",
"telefoneComercial": "6122222222",
"endereco": {
"pais": "Brasil",
"uf": "MG",
"cidade": "Belo Horizonte",
"logradouro": "Rua Teste",
"numero": "999",
"bairro": "Bairro Teste",
"cep": "85100000"
},
"emissaoNFeProduto": {
"ambienteProducao": {
"sequencialNFe": 1,
"serieNFe": "10"
}
}
}| Field | Type | Required | Description |
|---|---|---|---|
cnpj | string | yes | 14 digits |
inscricaoMunicipal | string | no | Municipal registration, up to 15 characters |
inscricaoEstadual | string | yes | State registration (IE). NF-e issuance requires an IE; missing returns 10003006 |
razaoSocial | string | yes | Legal name, up to 60 |
nomeFantasia | string | no | Trade name, up to 60 |
optanteSimplesNacional | boolean | yes | Whether the company is in the Simples Nacional regime |
mei | boolean | yes | Whether the company is an MEI. Tax regime derivation: mei=true → MEI; else optanteSimplesNacional=true → Simples; else regular regime |
email | string | yes | Contact e-mail |
telefoneComercial | string | yes | Business phone, digits only including area code |
endereco.pais | string | yes | Country |
endereco.uf | string | yes | State code, 2 letters |
endereco.cidade | string | yes | City name; the platform resolves the IBGE municipality code from it — unknown or ambiguous within the state returns GW001 |
endereco.logradouro | string | yes | Street |
endereco.numero | string | yes | Street number, up to 16 |
endereco.complemento | string | no | Additional info |
endereco.bairro | string | yes | District |
endereco.cep | string | yes | Postal code, 8 digits |
emissaoNFeProduto.ambienteProducao.sequencialNFe | number | yes | The next NF-e number to issue (from 1); the platform manages the sequence automatically afterwards |
emissaoNFeProduto.ambienteProducao.serieNFe | string | yes | NF-e series (1–3 digits); a dedicated series for invoices issued through the platform is recommended |
Response (HTTP 200)
{ "empresaId": "1934811222334455" }| Field | Type | Description |
|---|---|---|
empresaId | string | Company identifier, the path variable of every later API — persist it. A numeric string |
A successful registration enters the approval queue; registering the same CNPJ again
returns HTTP 400 with codigo 10003002.
Digital certificate association
POST /openapi/v1/empresas/{empresaId}/certificadoDigital
Content-Type: multipart/form-data| Form field | Type | Required | Description |
|---|---|---|---|
senha | text | yes | Certificate password |
arquivo | file | yes | A1 digital certificate file (.pfx / .p12) |
Platform checks: password matches (CER0005 otherwise), certificate not expired
(10003011), certificate CNPJ matches the company (10003010), not identical to the currently
active certificate (10003012). Success is HTTP 200 with no body. A successful upload
replaces the company’s previous certificate.
Signing note: for multipart requests the body is the empty string.
APP_SECRET="<APP_SECRET>"
P="/openapi/v1/empresas/1934811222334455/certificadoDigital"
TS=$(date +%s)
SIGN=$(printf '%s' "${APP_SECRET}${P}${TS}" | md5sum | cut -d' ' -f1)
curl -X POST "https://api.v2.tffiscal.com${P}" \
-H "token: ${APP_SECRET}" -H "timestamp: ${TS}" -H "sign: ${SIGN}" \
-F "senha=certpass123" -F "arquivo=@uploaded-cert.pfx"Webhook registration
POST /openapi/v1/webhooks
Content-Type: application/jsonRequest
{
"uri": "https://example.com/tffiscal/callback",
"contentType": "application/json",
"token": "dGt6eXp5ZGRra2tzc3Nra2hoaGFha2tha2FhamFoaGFoNzc3Nz"
}| Field | Type | Required | Description |
|---|---|---|---|
id | string | required for update | The webHookId returned on creation; omit to create, include to update (must belong to this application) |
uri | string | yes | Public https/http URL that receives callbacks |
contentType | string | yes | Only application/json is supported at present |
token | string | yes | Verification token of your choosing; sent back verbatim in the token request header of every callback so your receiver can verify the origin |
Response (HTTP 200)
{ "webHookId": "550001" }One callback configuration per application: webHookId is the application identifier,
and calling again overwrites the configuration. Registration automatically subscribes to
the two result events, authorized and denied. An invalid registration (id mismatch or a
non-JSON contentType) returns 10009033. The callback payload is documented in
Webhooks — Issuance result callback.
NF-e issuance
POST /openapi/v2/empresas/{empresaId}/nf-e
Content-Type: application/jsonAccepted requests return HTTP 200 with no body and enter the asynchronous issuance
flow; the result arrives via webhook or the query endpoint. Re-submitting the same id
within the idempotency window reuses the original task.
Request
{
"id": "NFe-000014553",
"ambienteEmissao": "Homologacao",
"pedido": {
"presencaConsumidor": "OperacaoPelaInternet",
"pagamento": {
"formas": [{
"tipo": "CartaoDeCredito",
"valor": 28.47,
"credenciadoraCartao": {
"tipoIntegracaoPagamento": "NaoIntegradoAoSistemaDeGestao",
"bandeira": "Mastercard"
}
}]
}
},
"cliente": {
"tipoPessoa": "F",
"nome": "Demo Client",
"email": "demo.client@mail.com",
"cpfCnpj": "88533234775",
"telefone": "(41) 3278-0217",
"endereco": {
"uf": "PR",
"cidade": "4106902",
"logradouro": "Rua Presidente Wilson",
"numero": "911",
"bairro": "Uberaba",
"cep": "81570440"
}
},
"itens": [{
"cfop": "6403",
"codigo": "000068",
"descricao": "Kingston DataTraveler SE9 DTSE9H 16GB USB Drive",
"ncm": "85235190",
"ean": "619659000424",
"quantidade": 1,
"unidadeMedida": "UN",
"valorUnitario": 28.47,
"impostos": {
"icms": { "situacaoTributaria": "101" },
"pis": { "situacaoTributaria": "49" },
"cofins": { "situacaoTributaria": "49" }
}
}]
}| Field | Type | Required | Description |
|---|---|---|---|
id | string | yes | Unique issuance request id (generated by you, up to 64); also the nfeId for query / cancellation and the idempotency key |
ambienteEmissao | string | yes | Homologacao / Producao; must match the company’s current environment |
pedido.presencaConsumidor | string | yes | Only OperacaoPelaInternet (internet sale) is supported at present |
pedido.pagamento.formas[] | array | yes | Payment methods — exactly one entry at present |
pedido.pagamento.formas[].tipo | string | yes | See the payment type table below |
pedido.pagamento.formas[].valor | number | yes | Payment amount |
pedido.pagamento.formas[].credenciadoraCartao | object | no | Card processor info (tipoIntegracaoPagamento / bandeira); accepted but not written into the NF-e |
cliente.tipoPessoa | string | yes | F individual / J legal entity; must be consistent with the length of cpfCnpj |
cliente.nome | string | yes | Name, up to 60 |
cliente.email | string | no | |
cliente.cpfCnpj | string | yes | CPF (11 digits) or CNPJ (14 digits), digits only |
cliente.telefone | string | no | Phone; accepted but not written into the NF-e |
cliente.endereco.pais | string | no | Country |
cliente.endereco.uf | string | yes | State code |
cliente.endereco.cidade | string | yes | IBGE 7-digit municipality code (note: the registration endpoint takes a city name) |
cliente.endereco.logradouro | string | yes | Street, 2–60 |
cliente.endereco.numero | string | yes | Street number |
cliente.endereco.complemento | string | no | Additional info |
cliente.endereco.bairro | string | yes | District, 2–60 |
cliente.endereco.cep | string | yes | Postal code, 8 digits |
itens[].cfop | string | yes | CFOP, 4 digits |
itens[].codigo | string | yes | Item code, up to 60 |
itens[].descricao | string | yes | Item description, up to 120 |
itens[].ncm | string | yes | NCM, 8 digits |
itens[].ean | string | no | GTIN barcode; SEM GTIN is written when absent |
itens[].cest | string | no | CEST, 7 digits (required for ST goods) |
itens[].quantidade | number | yes | Quantity, greater than 0 |
itens[].unidadeMedida | string | yes | Unit of measure, up to 6 |
itens[].valorUnitario | number | yes | Unit price |
itens[].impostos.icms.situacaoTributaria | string | yes | ICMS tax code: 2-digit CST or 3-digit CSOSN |
itens[].impostos.pis.situacaoTributaria | string | yes | PIS CST, 2 digits |
itens[].impostos.cofins.situacaoTributaria | string | yes | COFINS CST, 2 digits |
The line total is computed by the platform as quantidade × valorUnitario rounded to two
decimals; do not send it.
Tax codes and rates: the request carries tax codes only. Combinations that need no rate — CSOSN 102 / 103 / 300 / 400 for Simples Nacional companies together with PIS / COFINS 49 — issue directly; codes that require a rate (for example the ICMS rate for CST 00 under the regular regime, or the credit percentage for CSOSN 101) are currently rejected by the tax engine with 10005000 and will be enabled once the rate table is connected.
formas[].tipo values:
| Value | Meaning | Value | Meaning |
|---|---|---|---|
Dinheiro | Cash | ValeCombustivel | Fuel voucher |
Cheque | Cheque | BoletoBancario | Bank slip (boleto) |
CartaoDeCredito | Credit card | DepositoBancario | Bank deposit |
CartaoDeDebito | Debit card | PagamentoInstantaneoPix | Pix |
CreditoLoja | Store credit | TransferenciaBancaria | Bank transfer |
ValeAlimentacao | Food voucher | ProgramaDeFidelidade | Loyalty programme |
ValeRefeicao | Meal voucher | SemPagamento | No payment |
ValePresente | Gift voucher | Outros | Other |
Scope of this phase
| Item | Support |
|---|---|
| Invoice purpose | Regular sales invoices only (finNFe=1) |
| Consumer presence | OperacaoPelaInternet only |
| Payment methods | One entry; card processor info not written into the NF-e |
| Freight | Fixed to no transport (modFrete=9) |
| Tax rates | Tax codes only; rate-free combinations issue directly, rate-dependent codes await the rate table |
| DANFE link | Available in the query response; absent from the callback payload |
digestValue / customer phone / address complement | Not provided at present |
Values outside this range (presencaConsumidor, multiple payments, unknown payment type,
tipoPessoa inconsistent with the document) are rejected with 10004031.
NF-e query
GET /openapi/v2/empresas/{empresaId}/nf-e/{nfeId}nfeId is the id sent at issuance. Unknown ids return HTTP 404 with codigo NFe0001.
Response (HTTP 200)
{
"id": "NFe-000014553",
"tipo": "NF-e",
"status": "Autorizada",
"ambienteEmissao": "Homologacao",
"enviadaPorEmail": false,
"dataCriacao": "2024-11-27T20:56:46Z",
"dataUltimaAlteracao": "2024-11-27T20:56:56Z",
"forcarEmissaoContingencia": false,
"pedido": {
"presencaConsumidor": "OperacaoPelaInternet",
"pagamento": {
"tipo": "PagamentoAVista",
"formas": [{ "tipo": "CartaoDeCredito", "valor": 28.47 }]
}
},
"cliente": {
"indicadorContribuinteICMS": "NaoContribuinte",
"tipoPessoa": "F",
"nome": "Demo Client",
"email": "demo.client@mail.com",
"cpfCnpj": "88533234775",
"endereco": {
"uf": "PR",
"cidade": "Curitiba",
"logradouro": "Rua Presidente Wilson",
"numero": "911",
"bairro": "Uberaba",
"cep": "81570440"
}
},
"numero": "262",
"serie": "10",
"dataEmissao": "2024-11-27T20:56:46Z",
"chaveAcesso": "35241104893402000113550020000002621202427463",
"transporte": { "frete": { "modalidade": "SemFrete", "valor": 0 } },
"dataAutorizacao": "2024-11-27T20:56:55Z",
"linkDanfe": "https://api.v2.tffiscal.com/open/files/8802?token=eyJh...",
"linkDownloadXml": "https://api.v2.tffiscal.com/open/files/8801?token=eyJh...",
"linkConsultaPorChaveAcesso": "",
"protocolo": { "numero": "135240009194753" },
"emitidaEmContingencia": false,
"itens": [{
"cfop": "6403",
"codigo": "000068",
"descricao": "Kingston DataTraveler SE9 DTSE9H 16GB USB Drive",
"ncm": "85235190",
"quantidade": 1,
"unidadeMedida": "UN",
"valorUnitario": 28.47,
"valorTotal": 28.47
}],
"valorTotal": 28.47
}Top-level fields:
| Field | Type | Description |
|---|---|---|
id | string | Issuance request id |
tipo | string | Always NF-e |
status | string | AguardandoAutorizacao processing / Autorizada authorized / Negada denied / Cancelada cancelled |
motivoStatus | string | Denial reason: SEFAZ status code + description, e.g. 778 - Rejeicao: NCM inexistente; present only for Negada |
ambienteEmissao | string | Homologacao / Producao |
enviadaPorEmail | boolean | Always false (the platform does not e-mail invoices) |
dataCriacao | string | Time the issuance request was accepted, ISO-8601 UTC |
dataUltimaAlteracao | string | Last update time |
forcarEmissaoContingencia | boolean | Always false |
pedido | object | Order info — see below |
cliente | object | Customer info — see below |
numero | string | Invoice number nNF (string) |
serie | string | Series |
dataEmissao | string | Issuance time |
chaveAcesso | string | 44-digit access key |
transporte.frete.modalidade | string | Freight modality, currently always SemFrete |
transporte.frete.valor | number | Freight cost, currently always 0 |
dataAutorizacao | string | SEFAZ authorization time; empty until authorized |
linkDanfe | string | DANFE PDF download link (10-minute token), Autorizada only; the first query triggers rendering |
linkDownloadXml | string | Authorized XML download link (10-minute token), when an XML exists |
linkConsultaPorChaveAcesso | string | Always the empty string |
protocolo.numero | string | Authorization protocol number |
protocolo.digestValue | string | Signature digest, not provided at present |
emitidaEmContingencia | boolean | Whether issued in contingency mode |
itens[] | array | Line items: cfop / codigo / descricao / ncm / ean / cest / quantidade / unidadeMedida / valorUnitario / valorTotal / impostos |
valorTotal | number | Invoice total |
informacoesAdicionais | string | Additional information, not provided at present |
informacoesAdicionaisFisco | string | Additional information for the tax authority, not provided at present |
pedido:
| Field | Type | Description |
|---|---|---|
presencaConsumidor | string | Always OperacaoPelaInternet |
pagamento.tipo | string | Always PagamentoAVista |
pagamento.formas[].tipo | string | Payment type (as sent at issuance) |
pagamento.formas[].valor | number | Payment amount = invoice total |
pagamento.formas[].cnpjEstabelecimentoPagamento | string | Acquirer CNPJ, not provided at present |
pagamento.formas[].ufEstabelecimentoPagamento | string | Acquirer state, not provided at present |
intermediadorTransacao | object | Transaction intermediary, not provided at present |
cliente:
| Field | Type | Description |
|---|---|---|
indicadorContribuinteICMS | string | NaoContribuinte / ContribuinteICMS |
tipoPessoa | string | F / J (derived from the document length) |
nome / email / cpfCnpj | string | As sent at issuance |
inscricaoMunicipal / inscricaoEstadual | string | Not provided at present |
telefone | string | Not provided at present |
endereco.uf | string | State code |
endereco.cidade | string | City name (the issuance request carries the IBGE code; it is resolved to the name here) |
endereco.logradouro / numero / bairro / cep | string | As sent at issuance |
endereco.complemento | string | Not provided at present |
Status semantics: AguardandoAutorizacao from acceptance until SEFAZ replies;
Autorizada once authorized; Negada when rejected (motivoStatus explains why — fix
and resubmit under a new id); Cancelada after a successful cancellation.
NF-e cancellation
DELETE /openapi/v2/empresas/{empresaId}/nf-e/{nfeId}Cancels an authorized invoice; success is HTTP 200 with no body, and the query
endpoint then returns Cancelada. Rules:
- Only
Autorizadainvoices can be cancelled; other statuses return 10004012. - Within 24 hours of authorization (some states allow more, per platform configuration); outside the window returns 10004013, and only a return invoice can be issued instead.
- A SEFAZ refusal returns 10004014 with the SEFAZ status code and reason.
- Unknown
nfeIdreturns HTTP 404 withcodigoNFe0001.
Business error codes
| codigo | HTTP | Scenario | Action |
|---|---|---|---|
GW001 | 400 | Registration: city / state cannot be resolved to an IBGE code; issuance: customer IBGE municipality code does not exist | Check the UF and city name / IBGE code |
CER0005 | 400 | Certificate password mismatch | Check the password |
NFe0001 | 404 | The nfeId for query / cancellation does not exist | Check the id sent at issuance and the empresaId |
| 10003002 | 400 | CNPJ already registered | The company exists — use the original empresaId |
| 10003000 | 404 | empresaId does not exist or does not belong to this application | Check the empresaId |
| 10003006 | 400 | Registration data missing the IE | Provide inscricaoEstadual |
| 10003010 / 10003011 / 10003012 | 400 | Certificate CNPJ mismatch / expired / identical to the current one | Use the correct certificate |
| 10004004 | 400 | Company not issuable (not yet approved or certificate not ready) | Wait for approval / link the certificate |
| 10004030 | 400 | ambienteEmissao does not match the company’s current environment | Submit under the company environment or ask operations to switch it |
| 10004031 | 400 | Value not supported in this phase (presencaConsumidor / multiple payments / unknown payment type / tipoPessoa inconsistent with the document) | Adjust to the supported range |
| 10004002 | 400 | Too many pending issuance tasks for the CNPJ | Retry later |
| 10004012 | 400 | Cancellation: invoice not in authorized status | Query to confirm the status |
| 10004013 | 400 | Cancellation: outside the 24-hour window | Issue a return invoice instead |
| 10004014 | 400 | Cancellation: refused by SEFAZ (status code and reason attached) | Act on the SEFAZ reason |
| 10005000 | 400 | Insufficient tax parameters (e.g. a tax code that needs a rate) | See “Tax codes and rates” above |
| 10009033 | 400 | Invalid webhook registration (id mismatch / non-JSON contentType) | Fix as described |
| 10001001 | 400 | Request field validation failed (one entry per field) | Fix per mensagem |
Authentication-layer errors (401 / 403 / 429) use the platform envelope — see Authentication & Signing. The full cross-domain table is in Error Codes.
Integration checklist
- Register a company → 200 +
empresaId; register the same CNPJ again → 400 + 10003002. - Link the certificate → 200 with no body; wrong password → 400 +
CER0005. - Register the webhook → 200 +
webHookId. - After approval, issue with
ambienteEmissao=Homologacao→ 200 with no body; then query →AguardandoAutorizacaobecomesAutorizada, andlinkDanfe/linkDownloadXmldownload successfully. - Receive the authorized callback: the
tokenheader equals the registered value and the payload hasnfeStatus=Autorizada. - Negative cases:
ambienteEmissao=Producao→ 400 + 10004030;presencaConsumidor=OperacaoPresencial→ 400 + 10004031. - Cancel the just-authorized invoice → 200; query again →
Cancelada; cancel an unknown id → 404 +NFe0001. - Failure paths: wrong
sign→ 401 + 10009003; unsubscribed endpoint → 403 + 10009005.
Troubleshooting
Issuance stuck in AguardandoAutorizacao? While the company is in the test
environment, this depends on the availability of the SEFAZ test environment; if it
persists for more than a few minutes, contact the platform with the empresaId and
nfeId.
Issuance returns 10004004? The company has not been approved yet, or its certificate is not linked / has expired. Complete approval and certificate association after registration first.
Callbacks not arriving? Make sure uri is a publicly reachable https/http address
that returns 2xx; the platform retries with backoff and trips a circuit breaker after
consecutive failures — calling the registration endpoint again restores delivery.