Emissão RTC (Reforma Tributária)
A NFS-e no layout da Reforma Tributária do Consumo (RTC) usa o mesmo método da emissão comum:
client.service_invoices.create (ou create_and_wait). Não há recurso, URL nem cabeçalho
diferente. O que liga o layout RTC é a presença do grupo ibsCbs no corpo. Com ele, a API
valida o grupo na hora e grava os códigos da Reforma na nota.
O tipo ServiceInvoiceCreateParams já aceita ibsCbs e nbsCode. Consulta, cancelamento, PDF e
XML funcionam igual.
O que foi provado
Em 10/10/2026, notas RTC foram emitidas e canceladas com o SDK 0.1.0 numa empresa de teste (Simples Nacional, ambiente de testes):
| Cenário | Resultado |
|---|---|
Payload mínimo com ibsCbs | 202, Issued em ~5 s; ibsCbs e nbsCode devolvidos na consulta |
| Com ISS retido e retenções federais | Issued em ~4 s; retido 8,15 e líquido 91,85 numa nota de 100,00 |
operationIndicator com 7 dígitos | 400: ibsCbs.operationIndicator must be exactly 6 digits. Nenhuma nota criada |
Sem nbsCode | Aceito no ambiente de testes (veja o alerta abaixo) |
CNPJ do tomador como str com zeros à esquerda | Zeros preservados no XML |
cancel_and_wait | Cancelled em ~3,5 s |
Payload mínimo
from decimal import Decimal
from nfeio import NfeClient
from nfeio.types import ServiceInvoiceCreateParams
client = NfeClient()
company_id = "ID_DA_EMPRESA"
params: ServiceInvoiceCreateParams = {
"cityServiceCode": "10677", # código de serviço do município
"federalServiceCode": "01.01", # item da LC 116, formato NN.NN
"nbsCode": "115021000", # NBS 1.1502.10.00, 9 dígitos sem pontos
"description": "Desenvolvimento de software sob encomenda",
"servicesAmount": Decimal("10.00"),
"ibsCbs": {
"operationIndicator": "100301", # indicador da operação: exatamente 6 dígitos
"classCode": "000001", # classificação tributária (cClassTrib): 6 dígitos
},
"borrower": {
"type": "LegalEntity",
"name": "Tomador Exemplo Ltda",
"federalTaxNumber": "11222333000181", # str preserva zeros à esquerda
"address": {
"country": "BRA",
"postalCode": "01310-200",
"street": "Avenida Paulista",
"number": "2000",
"district": "Bela Vista",
"city": {"code": "3550308", "name": "São Paulo"},
"state": "SP",
},
},
}
invoice = client.service_invoices.create_and_wait(
company_id, params, external_id="pedido-rtc-1001"
)
print(invoice.flow_status) # "Issued"
print(invoice["ibsCbs"]["operationIndicator"], invoice["nbsCode"])
O que cada campo pede:
| Campo | Formato | Observação |
|---|---|---|
ibsCbs.operationIndicator | 6 dígitos, como str | Define o local de incidência e qual endereço a nota precisa trazer. 100301 (serviço prestado a distância) exige o endereço do tomador. |
ibsCbs.classCode | 6 dígitos, como str | Classificação tributária do IBS/CBS (000001 é tributação integral). Os 3 primeiros dígitos são o CST. |
nbsCode | 9 dígitos, sem pontos | Precisa ser coerente com o item da LC 116 em federalServiceCode. |
federalServiceCode | NN.NN | Item da LC 116. |
borrower.address | postalCode, street, city.code (IBGE, 7 dígitos), city.name, state | Obrigatórios quando um endereço brasileiro é informado. |
Os valores de IBS e CBS não são enviados: a NFE.io calcula a partir de operationIndicator,
classCode e servicesAmount. Os campos opcionais do grupo (purpose, destinationIndicator,
compras governamentais, locação de bens móveis etc.) estão na
documentação funcional do layout RTC.
O par federalServiceCode × nbsCode × operationIndicator × classCode sugerido para cada
serviço está na tabela de correlação LC 116 × NBS,
e os indicadores, na tabela de indicador da operação.
Alguns exemplos de payload RTC publicados usam operationIndicator com 7 dígitos ("1005011"). A
API recusa com 400 e o SDK levanta InvalidRequestError. Use sempre 6 dígitos, como texto.
Com retenções
As retenções continuam nos campos da raiz durante a transição, e convivem com o ibsCbs. Alíquotas
são frações (0.02 = 2%):
from decimal import Decimal
from nfeio import NfeClient
from nfeio.types import ServiceInvoiceCreateParams
client = NfeClient()
params: ServiceInvoiceCreateParams = {
"cityServiceCode": "10677",
"federalServiceCode": "01.01",
"nbsCode": "115021000",
"description": "Consultoria em tecnologia",
"servicesAmount": Decimal("100.00"),
"ibsCbs": {"operationIndicator": "100301", "classCode": "000001"},
"retentionType": "WithheldByBuyer", # ISS retido pelo tomador
"issRate": Decimal("0.02"),
"issAmountWithheld": Decimal("2.00"),
"irAmountWithheld": Decimal("1.50"),
"pisAmountWithheld": Decimal("0.65"),
"cofinsAmountWithheld": Decimal("3.00"),
"csllAmountWithheld": Decimal("1.00"),
"borrower": {
"type": "LegalEntity",
"name": "Tomador Exemplo Ltda",
"federalTaxNumber": "11222333000181",
"address": {
"country": "BRA",
"postalCode": "01310-200",
"street": "Avenida Paulista",
"number": "2000",
"district": "Bela Vista",
"city": {"code": "3550308", "name": "São Paulo"},
"state": "SP",
},
},
}
invoice = client.service_invoices.create_and_wait(
"ID_DA_EMPRESA", params, external_id="pedido-rtc-1002"
)
print(invoice.amount_net) # 91.85 (Decimal)
print(invoice["amountWithheld"]) # 8.15 (float, como no JSON)
if str(invoice["retentionType"]).lower() != "withheldbybuyer":
print("retenção diferente da enviada")
- Retenção de PIS/COFINS vai em
pisAmountWithheld/cofinsAmountWithheld, nunca empisAmount/cofinsAmount, que são o débito próprio. - Se nenhum campo de imposto federal for informado, a NFE.io calcula as retenções pela configuração da empresa. Envie os campos só quando o seu sistema já tiver os valores.
- A API devolve
retentionTypecom outra caixa (withheldByBuyer): compare sem diferenciar maiúsculas e minúsculas. - Não existe campo de retenção de IBS/CBS no layout.
Lendo a nota RTC
Os campos da Reforma não têm propriedade tipada na versão 0.1: leia pelo nome do JSON, como
invoice["ibsCbs"], invoice["nbsCode"], invoice["federalServiceCode"] e
invoice["amountWithheld"]. Valores com propriedade tipada (invoice.amount_net,
invoice.services_amount, invoice.iss_tax_amount) voltam como Decimal.
O CNPJ do tomador volta da API como número, sem os zeros à esquerda. Use a propriedade
invoice.borrower.federal_tax_number, que devolve str com 11 ou 14 dígitos.
O que depende da empresa
O contrato da API está provado. O resto depende da parametrização da empresa na NFE.io, e não do payload:
- Para onde a nota vai. A emissão pelo Ambiente Nacional depende do cadastro da inscrição
municipal da empresa ("Emitir via Emissor Nacional de NFS-e"). Na empresa de teste, que não
está no Emissor Nacional, a nota saiu no modelo municipal, com
CodigoNbsmas sem as tags de IBS/CBS no XML. - Cálculo de IBS/CBS. Nenhuma das notas de teste trouxe valores de IBS/CBS calculados. Isso é coerente com uma empresa do Simples Nacional em ambiente de testes: optantes pelo Simples Nacional e MEI, num primeiro momento, não testam as novas tags. Para validar o cálculo, use uma empresa do Lucro Presumido ou Real habilitada no Emissor Nacional, parametrizada pelo suporte.
nbsCode. A API aceitou nota sem NBS no ambiente de testes, mas o Ambiente Nacional rejeita NBS ausente ou incoerente com o item da LC 116. Envie sempre.- ISS. A alíquota vem do cadastro do código de serviço da empresa. Na empresa de teste (Simples
Nacional, alíquota zero),
issTaxAmountvoltou0.
Veja também
- Reforma Tributária: visão geral.
- Referência da API de NFS-e RTC.
- Notas de serviço: todos os métodos.