Quick Start
This guide walks through the five calls every issuing integration makes, in order:
- Register the company that will issue invoices and obtain its
empresaId. - Link the company’s A1 digital certificate.
- Register a webhook to receive issuance results.
- Issue an NF-e.
- Query the NF-e for its status and download links.
All requests go to the production base URL https://api.v2.tffiscal.com; the full URL is
the base URL plus the path shown for each call.
Prerequisites
- Application credential — an
app_secretissued for your application. It is displayed only once; store it securely. If it leaks, request a rotation. - API subscription — the platform grants your application access to every endpoint used below. Calling an endpoint you are not subscribed to returns HTTP 403 with code 10009005.
- Signing — every request carries the
token,timestampandsignheaders. The helper below is used by all examples on this page; the full specification is in Authentication & Signing. If this is your first call, verify your signing implementation against the echo endpoint first (see Verify signing with echo).
HOST="https://api.v2.tffiscal.com"
APP_SECRET="<APP_SECRET>"
# sign = lowercase hex MD5( token + path + body-without-CR-LF + timestamp )
# usage: sign "<path>" "<body>" "<timestamp>" (pass "" as body for GET / DELETE / multipart)
sign() {
printf '%s%s%s%s' "$APP_SECRET" "$1" "$(printf '%s' "$2" | tr -d '\r\n')" "$3" \
| md5sum | awk '{print $1}'
}Step 1 — Register the company
POST /openapi/v2/empresas submits the seller’s company data. The response carries the
empresaId that every later call uses as a path variable — persist it.
API_PATH="/openapi/v2/empresas"
BODY='{"cnpj":"14422279000106","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"}}}'
TS=$(date +%s)
curl -sS -X POST "$HOST$API_PATH" \
-H "Content-Type: application/json" \
-H "token: $APP_SECRET" -H "timestamp: $TS" -H "sign: $(sign "$API_PATH" "$BODY" "$TS")" \
-d "$BODY"{ "empresaId": "1934811222334455" }Registration places the company in the platform’s approval queue. The company can
issue only after operations approve it and the certificate is linked; registering the same
CNPJ twice returns HTTP 400 with codigo 10003002. Field-by-field reference:
Issuance API — Company registration.
Step 2 — Link the digital certificate
POST /openapi/v1/empresas/{empresaId}/certificadoDigital is a multipart/form-data
upload of the A1 certificate file and its password. For multipart requests the signed
body is the empty string.
EMPRESA_ID="1934811222334455"
API_PATH="/openapi/v1/empresas/$EMPRESA_ID/certificadoDigital"
TS=$(date +%s)
curl -sS -X POST "$HOST$API_PATH" \
-H "token: $APP_SECRET" -H "timestamp: $TS" -H "sign: $(sign "$API_PATH" "" "$TS")" \
-F "senha=certpass123" -F "arquivo=@certificate.pfx"Success is HTTP 200 with no body. A wrong password returns HTTP 400 with codigo
CER0005.
Step 3 — Register the webhook
POST /openapi/v1/webhooks registers the URL that receives issuance results. The
token you choose is sent back verbatim in the token request header of every callback,
so your receiver can verify the origin.
API_PATH="/openapi/v1/webhooks"
BODY='{"uri":"https://example.com/tffiscal/callback","contentType":"application/json","token":"dGt6eXp5ZGRra2tzc3Nra2hoaGFha2tha2FhamFoaGFoNzc3Nz"}'
TS=$(date +%s)
curl -sS -X POST "$HOST$API_PATH" \
-H "Content-Type: application/json" \
-H "token: $APP_SECRET" -H "timestamp: $TS" -H "sign: $(sign "$API_PATH" "$BODY" "$TS")" \
-d "$BODY"{ "webHookId": "550001" }There is one callback configuration per application; calling the endpoint again overwrites it. Registration subscribes you to the two result events, authorized and denied — see Webhooks.
Step 4 — Issue an NF-e
Once the company is approved, POST /openapi/v2/empresas/{empresaId}/nf-e accepts an
issuance request. The id is generated by you and is the key for query, cancellation and
idempotency. ambienteEmissao must match the company’s current environment; newly
registered companies start in Homologacao.
API_PATH="/openapi/v2/empresas/$EMPRESA_ID/nf-e"
BODY='{"id":"NFe-000014553","ambienteEmissao":"Homologacao","pedido":{"presencaConsumidor":"OperacaoPelaInternet","pagamento":{"formas":[{"tipo":"CartaoDeCredito","valor":28.47}]}},"cliente":{"tipoPessoa":"F","nome":"Demo Client","email":"demo.client@mail.com","cpfCnpj":"88533234775","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"}}}]}'
TS=$(date +%s)
curl -sS -X POST "$HOST$API_PATH" \
-H "Content-Type: application/json" \
-H "token: $APP_SECRET" -H "timestamp: $TS" -H "sign: $(sign "$API_PATH" "$BODY" "$TS")" \
-d "$BODY"An accepted request returns HTTP 200 with no body and enters the asynchronous issuance flow. The result arrives through the webhook registered in step 3, or through the query in step 5. Full request dictionary: Issuance API — NF-e issuance.
Step 5 — Query the NF-e
GET /openapi/v2/empresas/{empresaId}/nf-e/{nfeId} returns the current status, the
invoice data and, once authorized, the DANFE and XML download links. nfeId is the id
you sent in step 4. GET requests sign the empty string as body; the path variables are
part of the signed path.
NFE_ID="NFe-000014553"
API_PATH="/openapi/v2/empresas/$EMPRESA_ID/nf-e/$NFE_ID"
TS=$(date +%s)
curl -sS -X GET "$HOST$API_PATH" \
-H "token: $APP_SECRET" -H "timestamp: $TS" -H "sign: $(sign "$API_PATH" "" "$TS")"The status field moves from AguardandoAutorizacao to Autorizada (or Negada with a
motivoStatus explaining why). Response reference:
Issuance API — NF-e query.
Verify the failure modes
Each row is a one-line change to the sequence above and confirms your client fails the way you expect:
| Change | Expected result |
|---|---|
Wrong token value | HTTP 401, envelope code 10009002 (invalid token) |
Change BODY after computing sign | HTTP 401, envelope code 10009003 (signature mismatch) |
Reuse a timestamp older than 300 s | HTTP 401, envelope code 10009001 (timestamp) |
| Call an endpoint your app is not subscribed to | HTTP 403, envelope code 10009005 (not subscribed) |
| Register the same CNPJ twice | HTTP 400, [{"codigo":"10003002", ...}] |
Issue with ambienteEmissao set to Producao while the company is in test | HTTP 400, [{"codigo":"10004030", ...}] |
Query an unknown nfeId | HTTP 404, [{"codigo":"NFe0001", ...}] |
Authentication failures use the platform envelope; business failures of the issuance endpoints use an error array. Both shapes are described in General Conventions.
Next steps
- Issuance API — the six endpoints of the issuing domain, including cancellation.
- Verification API — verify third-party NF-e documents by XML or access key.
- Identity API — CNPJ and CPF registry lookups.
- Webhooks — callback payloads, signature verification and retries.
- Environments — how a company moves from test to production.