Notas fiscais de serviço (NFS-e) — beta
Com a API de NFS-e a sua empresa emite notas fiscais de serviço eletrônicas no Padrão Nacional a partir da marx: você envia um JSON simples e a marx monta, assina e transmite a nota ao Ambiente Nacional da NFS-e, guarda o XML e gera o PDF (DANFSe).
Importante
Beta, sob liberação. A NFS-e não está disponível publicamente: ela é liberada pelo time marx, por empresa. Sem a liberação, todos os endpoints respondem 403 com "NFS-e is not enabled for this company". Durante o beta, campos podem ser adicionados e mensagens de erro podem mudar; avisaremos antes de qualquer mudança incompatível.
Nota
Funciona para prestadores de municípios que emitem pelo Emissor Nacional (Padrão Nacional da NFS-e). Municípios com sistema próprio de nota ainda não são atendidos. NF-e de produtos não faz parte desta API.
Como funciona
- Você cadastra o emissor (o CNPJ que emite as notas), envia o certificado digital A1 desse CNPJ e cadastra os serviços que ele presta.
- Cada nota é pedida com
POST /api/nfsee umreferenceseu. A resposta é imediata (202) e a nota segue para a fila de emissão. - O resultado chega por webhook (
event_type_nfse_authorizedouevent_type_nfse_rejected) e também pode ser consultado na API. - Notas autorizadas têm PDF e XML disponíveis, e podem ser canceladas ou substituídas.
Você nunca envia XML nem códigos do leiaute fiscal: a marx traduz os campos (tabela no fim da página).
Configuração passo a passo
Todas as chamadas usam as mesmas credenciais da API de pagamentos (X-Client-Id e X-Client-Secret, veja Credenciais e autenticação).
1. Cadastre o emissor
POST /api/nfse/issuers
curl -X POST https://marx.loco.ltd/api/nfse/issuers \
-H "X-Client-Id: $MARX_CLIENT_ID" -H "X-Client-Secret: $MARX_CLIENT_SECRET" \
-H "Content-Type: application/json" -H "Accept: application/json" \
-d '{
"cnpj": "11.222.333/0001-81",
"legal_name": "Minha Empresa LTDA",
"municipal_registration": "0101010101",
"city_ibge": "4106902",
"tax_regime": "simples",
"simples_collection": "das",
"series": "900",
"approximate_tax_percent": "6.00",
"email": "fiscal@minhaempresa.com.br"
}'
| Campo | Obrigatório | Descrição |
|---|---|---|
cnpj |
sim | CNPJ do prestador (com ou sem pontuação; aceita o CNPJ alfanumérico). O dígito verificador é conferido. Não pode ser alterado depois. |
legal_name |
sim | Razão social. |
municipal_registration |
depende do município | Inscrição municipal como aparece no campo "Indicador Municipal" do Emissor Nacional (Emissão completa → Emitente). Pode ser diferente do número do cartão da prefeitura. |
send_municipal_registration |
não | true (padrão). Use false só se o seu município rejeitar a inscrição na nota. |
city_ibge |
não | Código IBGE do município do prestador (7 dígitos). Padrão: 4106902 (Curitiba). |
tax_regime |
não | simples (ME/EPP, padrão), mei ou regular (não optante). |
simples_collection |
não | Para o Simples: das (tributos federais e ISS pelo DAS, padrão), iss_outside_das ou all_outside_das. Confirme com o seu contador. |
special_regime |
não | Regime especial de tributação do município. 0 = nenhum (padrão). |
series |
não | Série da DPS, até 89999. Use uma série só para a marx, diferente da usada no portal do Emissor Nacional, para a numeração nunca se repetir. |
email |
não | E-mail do prestador na nota. |
auto_issue_on_payment |
não | true para emitir a nota automaticamente quando uma cobrança for paga (veja abaixo). |
email_customer |
não | true para enviar PDF e XML por e-mail ao tomador quando a nota for autorizada. |
iss_withholding_rate |
não | Alíquota (%) usada nas notas com ISS retido pelo tomador. Até 5%. |
description_footer |
não | Texto somado ao fim da descrição de toda nota, ex. "Documento emitido por ME ou EPP optante pelo Simples Nacional.". |
ibs_cbs |
a partir de 2027 | { "cst", "class_code", "operation_indicator" } padrão do emissor para o grupo IBS/CBS (veja IBS e CBS). |
approximate_tax_percent |
sim para ME/EPP | Percentual aproximado de tributos (Lei 12.741) padrão do emissor, ex. "6.00": a alíquota efetiva do Simples. O Ambiente Nacional recusa notas de ME/EPP sem ele (E0712). Cada serviço pode ter o seu. |
A resposta (201) traz o emissor com ready: false e a lista do que falta em missing: certificate, service, approximate_tax_percent (ME/EPP sem percentual) e activation. O emissor nasce inativo e em teste.
Dica
Nos caminhos /api/nfse/issuers/{id} você pode usar o id do emissor ou o próprio CNPJ só com números/letras (/api/nfse/issuers/11222333000181).
2. Envie o certificado digital A1
PUT /api/nfse/issuers/{id}/certificate
O certificado e-CNPJ A1 (ICP-Brasil) do próprio CNPJ do emissor assina as notas. Envie o arquivo .pfx (ou .p12) e a senha:
curl -X PUT https://marx.loco.ltd/api/nfse/issuers/11222333000181/certificate \
-H "X-Client-Id: $MARX_CLIENT_ID" -H "X-Client-Secret: $MARX_CLIENT_SECRET" \
-F file=@certificado-a1.pfx -F password="$SENHA_DO_CERTIFICADO"
Ou em JSON, com o arquivo em Base64: {"pfx_base64": "MIIK…", "password": "…"}.
O certificado só é aceito se abrir com a senha, contiver a chave privada, estiver dentro da validade e for um e-CNPJ do mesmo CNPJ do emissor (o Ambiente Nacional recusa notas assinadas com certificado de outro CNPJ, inclusive de contador ou procurador).
- O arquivo e a senha ficam guardados criptografados e nunca são devolvidos pela API. As respostas trazem só os dados públicos: titular, CNPJ, validade e impressão digital (
fingerprint_sha256). GET /api/nfse/issuers/{id}/certificatemostra esses dados;DELETEremove o certificado (o emissor para de emitir até receber outro).- Renovação: o A1 vale 1 ano. Envie o novo com o mesmo
PUT; ele substitui o anterior na hora. Guarde sempre uma cópia do.pfxe da senha no seu cofre de senhas.
3. Cadastre os serviços
POST /api/nfse/issuers/{id}/services
curl -X POST https://marx.loco.ltd/api/nfse/issuers/11222333000181/services \
-H "X-Client-Id: $MARX_CLIENT_ID" -H "X-Client-Secret: $MARX_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{ "code": "01.03.01", "nbs": "1.1506.21.00", "description": "Assinatura de software (SaaS)" }'
| Campo | Obrigatório | Descrição |
|---|---|---|
code |
sim | Código de tributação nacional do serviço (6 dígitos; pontuação é ignorada). |
nbs |
sim | Código NBS (9 dígitos). |
description |
sim | Texto padrão que vai na nota quando o pedido não traz descrição. |
municipal_code |
não | Código de tributação municipal (3 dígitos), quando o município exigir. |
iss_rate |
não | Alíquota de ISS em %, só quando o ISS não é recolhido pelo Simples. |
approximate_tax_percent |
não | Percentual aproximado de tributos deste serviço, quando o anexo do Simples dele é diferente do padrão do emissor. |
ibs_cbs |
não | { "cst", "class_code", "operation_indicator" } deste serviço (vazio = padrão do emissor). |
default |
não | Serviço padrão do emissor. O primeiro cadastrado já é o padrão. |
Os códigos e a NBS de cada serviço são definidos com o seu contador. GET lista os serviços, PATCH /api/nfse/issuers/{id}/services/{service_id} altera e DELETE remove um serviço que ainda não foi usado em nenhuma nota.
4. Ative o emissor
curl -X PATCH https://marx.loco.ltd/api/nfse/issuers/11222333000181 \
-H "X-Client-Id: $MARX_CLIENT_ID" -H "X-Client-Secret: $MARX_CLIENT_SECRET" \
-H "Content-Type: application/json" -d '{"active": true}'
A ativação só é aceita quando não falta mais nada: a resposta passa a trazer "ready": true e "missing": [].
5. Teste e produção
Todo emissor começa em teste ("environment": "test"): as notas vão para o ambiente de produção restrita do Emissor Nacional e não têm valor fiscal. A numeração de teste é separada, então os testes não deixam buracos na numeração real.
Faça os testes que precisar (emitir, consultar, cancelar, substituir). Quando estiver tudo certo, peça ao time marx para mudar o emissor para produção. A partir daí as notas são reais. Cada nota informa em que ambiente foi emitida (environment).
Atenção
Antes da primeira nota em produção, confirme com o seu contador o regime, os códigos de serviço, a NBS, a inscrição municipal e o texto obrigatório da descrição. Uma nota emitida com dados errados precisa ser cancelada ou substituída.
Emitir uma nota
POST /api/nfse
curl -X POST https://marx.loco.ltd/api/nfse \
-H "X-Client-Id: $MARX_CLIENT_ID" -H "X-Client-Secret: $MARX_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{
"reference": "fatura-2026-10-0042",
"amount": "149.90",
"competence_date": "2026-10-01",
"customer": {
"tax_id": "11.222.333/0001-81",
"name": "Cliente LTDA",
"email": "financeiro@cliente.com.br"
},
"service": { "code": "010301", "description": "Assinatura Pro — outubro/2026" }
}'
| Campo | Obrigatório | Descrição |
|---|---|---|
reference |
sim | Seu identificador da nota (fatura, pedido). Único para sempre por empresa: repetir o mesmo reference devolve a nota existente (200) e nunca emite outra. Também aceito no cabeçalho Idempotency-Key. |
amount |
sim | Valor do serviço, texto com ponto decimal. |
customer.tax_id |
sim | CPF (11) ou CNPJ (14) do tomador; aceita pontuação e CNPJ alfanumérico. |
customer.name |
sim | Nome ou razão social do tomador. |
customer.email |
não | Recebe o PDF e o XML se o emissor tiver email_customer. |
customer.country |
não | Código do país (ISO, 2 letras). Padrão BR. Outro país = tomador no exterior: tax_id passa a ser o identificador fiscal estrangeiro. |
customer.address |
não | postal_code, street, number, complement, district, city_ibge. |
service.code |
não | Um dos serviços do emissor. Padrão: o serviço padrão. |
service.description |
não | Texto da nota. Padrão: a descrição do serviço. |
competence_date |
não | Data de competência (AAAA-MM-DD). Padrão: hoje. |
issuer_id |
não | id ou CNPJ do emissor, se a sua empresa tiver mais de um. Padrão: o emissor ativo mais antigo. |
payment_id |
não | id de uma cobrança marx, para vincular a nota ao pagamento. |
approximate_tax_percent |
não | Percentual aproximado de tributos (Lei 12.741) só desta nota. Padrão: o do serviço ou o do emissor. |
export |
sim, tomador no exterior | Dados da exportação: veja Exportação de serviços. |
iss_withheld / iss_rate |
não | ISS retido pelo tomador: veja ISS retido. |
ibs_cbs |
não | Grupo IBS/CBS só desta nota (padrão: serviço → emissor). |
Resposta 202:
{
"object": "nfse",
"id": "8f1c2a7e-4b1d-4f43-9b2f-5e8d9a4b1c22",
"status": "queued",
"environment": "test",
"reference": "fatura-2026-10-0042",
"issuer": { "id": "…", "cnpj": "11222333000181" },
"number": null,
"access_key": null,
"amount": "149.9",
"competence_date": "2026-10-01",
"description": "Assinatura Pro — outubro/2026",
"service": { "code": "010301", "nbs": "115062100" },
"customer": { "tax_id": "11222333000181", "country": "BR", "name": "Cliente LTDA", "email": "financeiro@cliente.com.br" },
"errors": [],
"cancellation": null,
"replaces_id": null,
"replaced_by_id": null,
"links": {},
"created_at": "2026-10-06T10:00:00-03:00"
}
Se o emissor ainda não estiver pronto, a resposta é 422 com o que falta: {"error": "…", "missing": ["certificate"]}.
Exportação de serviços
Quando o tomador é do exterior (customer.country diferente de BR), a nota é de exportação: o ISS não incide (tribISSQN = 3), o tax_id é o identificador fiscal estrangeiro (NIF) e o grupo de comércio exterior é obrigatório. Envie export:
{
"reference": "consultoria-2026-10",
"amount": "24480.00",
"customer": { "tax_id": "98-7654321", "country": "US", "name": "Acme Inc" },
"service": { "code": "010601", "description": "Consultoria em TI — outubro/2026" },
"export": { "currency": "USD", "amount_in_currency": "4896.00", "mode": "cross_border" }
}
| Campo | Obrigatório | Descrição |
|---|---|---|
currency |
sim | USD, EUR, GBP ou o código numérico da moeda no Banco Central (3 dígitos). |
amount_in_currency |
sim | Valor do serviço na moeda estrangeira. amount continua em reais. |
mode |
não | cross_border (remoto, padrão), consumption_in_brazil, commercial_presence_abroad, temporary_movement_of_people. |
relationship |
não | Vínculo com o tomador: none (padrão), controlled, controlling, affiliate, head_office, branch, other. |
provider_support / customer_support |
não | Mecanismo de apoio ao comércio exterior, código da tabela nacional. Padrão 01 (nenhum). |
temporary_goods |
não | no (padrão), import_declaration, export_declaration. |
share_with_mdic |
não | true para compartilhar a nota com a Secretaria de Comércio Exterior. Padrão false. |
Nota
Exportação só vale quando o resultado do serviço acontece no exterior. Se parte do serviço é consumida no Brasil, emita notas separadas. Exportações não podem ter ISS retido. Confirme o enquadramento com o seu contador.
ISS retido pelo tomador
Quando o seu cliente retém o ISS (tpRetISSQN = 2), envie "iss_withheld": true. A alíquota vem de iss_rate na nota, ou da alíquota do serviço, ou de iss_withholding_rate do emissor (até 5%).
A API recusa (422) antes de enviar quando a regra nacional não permite:
| Regra | Código |
|---|---|
| Tomador sem CPF/CNPJ | E0204 |
Tomador sem endereço no Brasil (customer.address) |
E0237 |
| Alíquota acima de 5% | E0595 |
| Emissor MEI ou com regime especial de tributação | E0583 / E0588 |
| Exportação | E0580 |
Atenção
A retenção também depende das regras do município do tomador. Se o município não prevê retenção para o seu caso, o Ambiente Nacional recusa a nota.
IBS e CBS (Reforma Tributária)
A partir de 01/01/2027, toda NFS-e precisa do grupo IBS/CBS, inclusive de empresas do Simples Nacional. Em 2026 ele é opcional para o Simples. Configure os códigos no emissor (ibs_cbs), em cada serviço ou em uma nota específica:
| Campo | Descrição |
|---|---|
cst |
Código de Situação Tributária do IBS e da CBS (3 dígitos). |
class_code |
Código de Classificação Tributária, cClassTrib (6 dígitos). |
operation_indicator |
Indicador da operação, cIndOp (6 dígitos). Para serviços em geral, pagos, prestados a clientes no Brasil: 100301. |
- Configurado, o grupo vai em toda nota. Sem configuração ele é omitido até 31/12/2026.
- A partir de 2027, a API recusa a nota e o emissor aparece com
missing: ["ibs_cbs"]enquanto faltar. - Os valores de IBS e CBS do Simples são calculados pelo Ambiente Nacional; você informa só os códigos.
Importante
CST e cClassTrib dependem do seu regime e do serviço. Defina-os com o seu contador e teste no ambiente de teste antes de 2027.
Status da nota
status |
O que significa | O que fazer |
|---|---|---|
queued |
Recebida, na fila de emissão. | Aguardar o webhook. |
processing |
Enviada ao Ambiente Nacional. Instabilidades são repetidas automaticamente, sem risco de nota duplicada. | Aguardar. |
authorized |
Nota emitida: number, access_key, PDF e XML disponíveis. |
Entregar ao cliente. |
rejected |
Recusada pelo Ambiente Nacional. Os códigos e mensagens estão em errors. |
Corrigir os dados e pedir uma nova nota, com outro reference. |
cancelled |
Cancelamento registrado. | — |
replaced |
Substituída por outra nota (replaced_by_id). |
Use a nota nova. |
needs_data |
Nota automática de um pagamento sem CPF/CNPJ completo do pagador. | Fale com o time marx ou envie os dados do cliente na cobrança (veja abaixo). |
Consultar, listar e baixar
| Endpoint | O que faz |
|---|---|
GET /api/nfse/{id} |
A nota, no formato acima. |
GET /api/nfse |
Lista, da mais recente para a mais antiga. Filtros: status, issuer_id, reference, payment_id, competence_from, competence_to (AAAA-MM-DD). Paginação: page e per_page (até 100). |
GET /api/nfse/{id}/pdf |
PDF (DANFSe) da nota autorizada. Notas canceladas ou substituídas saem com marca d'água. |
GET /api/nfse/{id}/xml |
XML oficial da nota autorizada. |
Antes da autorização, PDF e XML respondem 404.
Cancelar
POST /api/nfse/{id}/cancel
curl -X POST https://marx.loco.ltd/api/nfse/8f1c2a7e-4b1d-4f43-9b2f-5e8d9a4b1c22/cancel \
-H "X-Client-Id: $MARX_CLIENT_ID" -H "X-Client-Secret: $MARX_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{ "reason": "service_not_provided" }'
| Campo | Descrição |
|---|---|
reason |
error (erro na emissão), service_not_provided (serviço não prestado) ou other (padrão). |
description |
Opcional, de 15 a 255 caracteres. Sem ela, usamos um texto padrão para o motivo. |
Responde 202 com cancellation.status = "pending"; quando o cancelamento é registrado, a nota fica cancelled e chega o webhook event_type_nfse_cancelled. Só notas authorized podem ser canceladas.
Atenção
Cada município define o prazo de cancelamento (em Curitiba, 60 dias e antes do pagamento do imposto). Fora do prazo, ou acima de certos valores, o pedido vira análise fiscal na prefeitura e pode ser recusado. Para dados errados numa nota válida, prefira substituir.
Substituir
POST /api/nfse/{id}/replace
Cria uma nota nova que substitui a original. Envie um reference novo e só os campos que mudam; os demais são copiados da original.
curl -X POST https://marx.loco.ltd/api/nfse/8f1c2a7e-4b1d-4f43-9b2f-5e8d9a4b1c22/replace \
-H "X-Client-Id: $MARX_CLIENT_ID" -H "X-Client-Secret: $MARX_CLIENT_SECRET" \
-H "Content-Type: application/json" \
-d '{ "reference": "fatura-2026-10-0042-v2", "service": { "description": "Assinatura Pro — outubro/2026 (contrato 123)" } }'
Importante
ME/EPP do Simples: a nota substituta não pode mudar o valor, a data de competência nem o CPF/CNPJ do tomador (regra nacional, código E0063). A API recusa esses pedidos com 422 e o campo locked. Para corrigir esses dados, cancele a nota e emita outra. Use a substituição para corrigir descrição, endereço, serviço e outros dados.
reason é opcional: other (padrão), rejected_by_customer, simples_exclusion, simples_inclusion, immunity_added ou immunity_removed. A resposta (202) é a nota nova, com replaces_id. Quando ela for autorizada, a original passa a replaced.
Emissão automática no pagamento
Com auto_issue_on_payment: true no emissor, a marx emite uma nota para cada cobrança paga (uma por pagamento, nunca duas). Para a nota sair completa, envie os dados do tomador e, se quiser, o serviço e a descrição no metadata da cobrança:
{
"metadata": {
"nfse": {
"customer": { "cnpj": "11222333000181", "name": "Cliente LTDA", "email": "financeiro@cliente.com.br" },
"service_code": "010301",
"description": "Assinatura Pro — outubro/2026"
}
}
}
Sem esses dados, usamos o pagador informado pela rede Pix quando ele vem completo; caso contrário a nota fica em needs_data. Devoluções não cancelam a nota automaticamente.
Webhooks
As notas usam o mesmo envio, assinatura e novas tentativas dos webhooks de pagamento. O payload é a nota (mesmo formato de GET /api/nfse/{id}).
event |
Quando |
|---|---|
event_type_nfse_authorized |
Nota autorizada. |
event_type_nfse_rejected |
Nota recusada (errors traz os códigos). |
event_type_nfse_cancelled |
Cancelamento registrado. |
event_type_nfse_replaced |
A nota foi substituída: enviado para a nota original quando a substituta é autorizada. |
event_type_nfse_cancellation_rejected |
O Ambiente Nacional recusou o cancelamento (prazo, análise fiscal…). cancellation.status = "rejected" e a nota segue autorizada. |
event_type_nfse_needs_data |
Nota automática de um pagamento aguardando o CPF/CNPJ do tomador. |
Erros comuns na emissão
Os códigos vêm do Ambiente Nacional, em errors[].code:
| Código | Causa provável |
|---|---|
E0116 / E0120 |
Inscrição municipal faltando, diferente do cadastro, ou enviada a um município que não a usa. Use o valor do campo Indicador Municipal do Emissor Nacional (Emissão completa → Emitente), que pode ser diferente do número do cartão da prefeitura. |
E0312 |
O código de serviço não é administrado pelo seu município. |
E0602 / E0617 |
Alíquota ou dados do Simples diferentes do cadastro municipal. |
E0712 |
ME/EPP sem percentual aproximado de tributos: configure approximate_tax_percent no emissor ou no serviço. |
E0063 |
Substituição de nota de ME/EPP alterando valor, competência ou tomador. Cancele e emita outra. |
E0330 / E0333 |
Exportação sem o grupo de comércio exterior, ou com valor "desconhecido". Envie export. |
E0204 / E0237 / E0595 / E0580 |
ISS retido sem documento ou endereço do tomador, alíquota acima de 5%, ou em exportação. |
MARX |
Recusada pela própria marx antes do envio, porque os dados não montam uma nota válida (a mensagem diz o que falta). |
E0714 a E0718 |
Certificado de outro CNPJ ou assinatura inválida. Envie o A1 do próprio emissor. |
E0038 / E0039 |
Empresa não habilitada no Emissor Nacional para o município. Faça o primeiro acesso no portal do Emissor Nacional. |
Como os campos viram a nota
| Campo da API | Campo da DPS (Padrão Nacional) |
|---|---|
emissor cnpj, municipal_registration |
prest/CNPJ, prest/IM |
tax_regime / simples_collection / special_regime |
regTrib/opSimpNac / regApTribSN / regEspTrib |
city_ibge |
cLocEmi, locPrest/cLocPrestacao |
series + numeração da marx |
serie, nDPS, Id da DPS |
customer.tax_id |
toma/CNPJ, toma/CPF ou toma/NIF (exterior) |
service.code, nbs, municipal_code |
cServ/cTribNac, cNBS, cTribMun |
service.description |
cServ/xDescServ |
amount |
vServPrest/vServ |
competence_date |
dCompet |
approximate_tax_percent (nota → serviço → emissor) |
totTrib/pTotTribSN |
export |
serv/comExt (mdPrestacao, vincPrest, tpMoeda, vServMoeda, mecAFComexP, mecAFComexT, movTempBens, mdic) |
iss_withheld + alíquota |
tribMun/tpRetISSQN = 2 + pAliq |
ibs_cbs |
IBSCBS (finNFSe = 0, cIndOp, indDest = 0, gIBSCBS/CST, cClassTrib) |
description_footer do emissor |
somado ao fim de cServ/xDescServ |
cancel reason |
evento de cancelamento cMotivo (error 1, service_not_provided 2, other 9) |
replace |
subst/chSubstda, cMotivo |
Checklist antes de produção
- [ ] Regime, códigos de serviço, NBS e texto da descrição confirmados com o contador.
- [ ] Inscrição municipal idêntica ao Indicador Municipal do Emissor Nacional (confira também no ambiente de produção).
- [ ] Percentual aproximado de tributos configurado (obrigatório para ME/EPP).
- [ ] Códigos de IBS/CBS configurados e testados antes de 01/01/2027.
- [ ] Exportação: notas de teste com
exportemitidas, se você tem clientes no exterior. - [ ] Série exclusiva da marx (diferente da usada no portal).
- [ ] Notas de teste emitidas, consultadas, canceladas e substituídas com sucesso.
- [ ] Webhooks
event_type_nfse_*tratados e assinatura validada. - [ ]
referenceúnico em todo pedido de nota. - [ ] Lembrete para renovar o certificado A1 antes do vencimento.
- [ ] Pedido ao time marx para mudar o emissor para produção.