Sudrel API
Dashboard Create API key

Documentation › NFS-e issuance

Guides

NFS-e issuance

Issue, validate, query, cancel and replace NFS-e, with automatic adjustments per municipality.

Issue NFS-e through the same engine as the spreadsheet and the dashboard: same validation, same queue, same recovery from technical failures. An invoice issued by the API appears in the dashboard, with the origin "API".

You always send the same JSON, whatever the municipality. What changes per municipality or per tax scenario (fill in, omit, require a field) is decided on our side, and the response tells what was done in ajustes:

{
  "campo": "/prestador/inscricao_municipal",
  "acao": "omitido",
  "motivo": "O município não aceita a inscrição municipal do prestador nesta DPS (E0120). A nota foi reenviada sem ela.",
  "regra": "correcao.E0120"
}

acao is preenchido, omitido, derivado or exigido; regra is the stable identifier of the rule in our catalog (for example dps.pAliq.exigido.simples_retido, param.aliquota_iss, correcao.E0120). regras_versao carries the version of the two catalogs that decided the invoice, automatic correction and field rules, in that order (2026-09-30.1+2026-10-02.1).

When the ISS tax rate is required and you send none (and the company has no default), we use the OFFICIAL tax rate that the municipality registered in the Sistema Nacional NFS-e for the service, if it is the only one in the accrual period (competência). It appears as a preenchido adjustment with the rule param.aliquota_iss.

Status

The full cycle, with what to do in each status and the matching webhook, is in NFS-e statuses.

Status What it means
transmitindo We are handling it: in the queue, sending, or recovering from a technical failure of the Sistema Nacional NFS-e. Do not resend.
autorizada NFS-e issued. numero and chave_acesso filled in.
rejeitada The Sistema Nacional NFS-e refused it. Reason in rejeicao. Fix it and issue a new invoice.
cancelada Cancelled after authorization. Protocol in cancelamento. If it was a replacement, substituida_por tells which invoice replaced it.
falha_transmissao Final. The transmission could not be completed or confirmed, and nothing else will be tried automatically. See falha: with dps_enviada: "desconhecido", check in the Emissor Nacional before issuing again.
descartada The invoice came back without success and was discarded in the dashboard.
aguardando_confirmacao The invoice's accrual period ended before an automatic retry. Nothing is transmitted until you confirm in POST /v1/nfse/{id}/confirmacao (you can change the accrual period).

If the invoice stays 30 minutes in transmitindo, the response gets aviso: {"codigo": "processamento_prolongado"} and the webhook NFSE_PROCESSAMENTO_PROLONGADO is sent, once. It is only a warning: the invoice is still being handled and the status does not change.

Replace an NFS-e

To correct an authorized invoice without cancelling it and issuing another, issue the correct invoice in POST /v1/nfse with the substituicao block:

{
  "empresa_id": "…",
  "tomador": { "documento": "11222333000181" },
  "valor": "1500.00",
  "servico": { "descricao": "Consultoria de setembro, valor corrigido" },
  "substituicao": { "nfse_id": "id-da-nota-original", "codigo_motivo": "99", "motivo": "Valor do serviço informado errado na nota original." }
}

The new invoice follows the usual path. When it is authorized, the Sistema Nacional NFS-e cancels the original by itself (cancellation by replacement): the original becomes cancelada, with substituida_por, and its NFSE_CANCELADA webhook is sent. If the new one is rejected, the original stays authorized and nothing changes in it.

Before transmitting, we refuse with 422 what is already known not to pass: invoice cancelled or already replaced (E0046), invoice from another environment (E0065), another replacement of the same invoice in progress, and codigo_motivo 99 without motivo (E0078). The municipality's replacement deadline (E0050) and the service recipient's (tomador) manifestation (E0070) are known only to the Sistema Nacional NFS-e; they come back as a rejection, with rejeicao.explicacao.

For an NFS-e issued outside Sudrel, use chave_acesso instead of nfse_id. Replacement does not exist in batches.

Civil construction: construction site data

For the civil construction codes (07.02.01, 07.02.02, 07.04.01, 07.05.01, 07.05.02, 07.06.01, 07.06.02, 07.07.01, 07.08.01, 07.17.01, 07.19.01, 14.14.03 and 14.14.04) the Sistema Nacional NFS-e requires the construction site data, and for the others it refuses it. Send servico.obra with the CNO, the CIB, or the CEP and street number of the site, and the municipality, when it does not come from the CEP:

{ "servico": { "codigo_nacional": "070201", "obra": { "cno": "90.012.34567/89", "municipio": "Lauro de Freitas - BA" } } }

The site's municipality becomes the invoice's place of service. For the codes where the ISS is due where the site is, that municipality's agreement decides whether the ISS tax rate is required. In the spreadsheet, these are the columns "CNO da obra", "CEP da obra" etc., in the Completo layout.

Testing the integration (sandbox)

Use a sdr_test_ key on the SAME routes and with the SAME JSON. The invoice is really transmitted to the test environment of the Sistema Nacional NFS-e ("restricted production", the national system's test environment), with the company's real certificate, and comes back authorized or rejected by the same rules. It has no fiscal value, does not count against the allowance, does not depend on a plan and does not appear in the dashboard. ambiente comes as sandbox in the response and in the webhooks.

The two environments never mix: the test key sees only test invoices, the production key only production ones, and the same referencia or Idempotency-Key can be used in both. A company that issues through the municipality's own system has no test environment: readiness returns the item sandbox_municipio_proprio in bloqueio, and issuance is refused with 412 empresa_nao_pronta.

A technical failure of the Sistema Nacional NFS-e is not a response for you: the invoice stays transmitindo while the automatic recovery works (it queries the DPS before any resend, so the invoice is never issued twice).

Minimal issuance

curl https://api.sudrel.com.br/api/v1/nfse \
  -H "Authorization: Bearer sdr_live_..." \
  -H "Idempotency-Key: 7f1c0d9a-4a51-4e0e-9b1f-2b6f0d3c5a10" \
  -H "Prefer: wait=15" \
  -H "Content-Type: application/json" \
  -d '{"empresa_id":"0b7e6a52-3c1d-4f8e-9a20-5d4c3b2a1f00","referencia":"PED-1001","tomador":{"documento":"11222333000181"},"valor":"1500.00","servico":{"descricao":"Consultoria em sistemas"}}'

Money values and tax rates go as TEXT with a decimal point ("1500.00"). A JSON number is refused, so that no invoice value depends on floating-point rounding.

Endpoints in this section, with in-page testing