Notas fiscais
Emita, consulte e acompanhe o ciclo de vida das NFS-e.
Permissões: nfse.invoices.issue para emitir/solicitar email; nfse.invoices.view para consultas, documentos e URL assinada.
/v1/invoices202 AcceptedSolicita a emissão de uma NFS-e. A emissão é assíncrona: o sucesso HTTP significa que a solicitação foi registrada/enfileirada, não autorização fiscal. Os exemplos usam o CNPJ (companyTaxId) da empresa cadastrada com municipalityCode e defaultServiceCode. O município do emitente sempre vem da empresa.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| companyId | string UUID | Condicional: informe este campo ou companyTaxId; se ambos existirem, companyId prevalece. | |
| companyTaxId | string | CNPJ da empresa já cadastrada na conta/ambiente; pontuação opcional. | |
| externalId | string | Referência do sistema integrador; não substitui a chave de idempotência. | |
| customer | objeto | Opcional: omita ou envie {} para uma nota sem tomador (o tomador não vai na DPS). Quando enviado com qualquer dado, taxId é obrigatório (CPF ou CNPJ válido, normalizado); name é opcional, email (opcional; obrigatório quando o envio de email está habilitado; sem validação de formato neste DTO) e address (opcional: street, city, state, zip, country; se preencher cidade ou UF, preencha ambas). | |
| service | objeto | ✓ | code é opcional: sem ele vale o defaultServiceCode da empresa e, se ela não tiver, 100901 (LC 116 item 10.09, representação de qualquer natureza); quando informado, exatamente 6 dígitos. municipalityCode herda o município da empresa; quando informado, exatamente 7 dígitos e define o local da prestação. description é opcional: se vazia, usa-se o nome do código de serviço. Vazio, null ou valor inválido dos demais campos não aciona o padrão. |
| amount | number | ✓ | Valor em reais, positivo, até 9999999999.99 e no máximo 2 casas decimais. |
| competenceDate | string ISO 8601 | ✓ | Data de competência válida. Sem options.type, não pode ser futura (422 ERROR_FUTURE_DATE). Com options.type: "SCHEDULE", aceita data futura (veja Notas agendadas). |
| discount | number | Desconto incondicional em reais, de 0 até o valor da nota, no máximo 2 casas decimais. | |
| deduction | number | Dedução da base do ISS declarada pelo emitente, em reais: maior que 0 e menor que amount menos discount, no máximo 2 casas decimais (0 equivale a omitir). Vai na DPS em vDedRed/vDR e reduz a base de cálculo do ISS. A nota continua sendo emitida pelo valor total. O emitente responde pelo valor declarado; a Corvo não confere se a dedução é permitida. O IBS/CBS segue calculado sobre o valor sem a dedução. Veja Dedução da base do ISS. | |
| boolean | Solicita notificação; padrão false. | ||
| options.email.send | boolean | Quando definido, prevalece sobre email. | |
| options.type | string | IMMEDIATE (padrão) ou SCHEDULE. SCHEDULE agenda a emissão para a competenceDate futura; veja Notas agendadas. |
curl -X POST "$BASE_URL/invoices" \
-H "X-API-Key: $CORVO_API_KEY" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: pedido_123_emissao' \
-d '{
"companyTaxId": "11222333000181",
"externalId": "pedido_123",
"customer": {
"name": "Cliente Exemplo",
"taxId": "52998224725",
"email": "cliente@example.com",
"address": { "city": "São Paulo", "state": "SP" }
},
"service": {
"description": "Prestação de serviço"
},
"amount": 150.50,
"competenceDate": "2026-10-01",
"options": { "email": { "send": true } }
}'Resposta — 202 Accepted
{
"data": {
"id": "c7d2e9a1-4b5f-4a6c-8d9e-0f1a2b3c4d5e",
"companyId": "3f1c8a90-6b2e-4d5a-9c11-8e2f0a7b4d11",
"environment": "test",
"externalId": "pedido_123",
"status": "queued",
"amount": 150.5,
"competenceDate": "2026-10-01T00:00:00.000Z",
"createdAt": "2026-10-10T12:00:00.000Z"
}
}/v1/invoices/batches202 AcceptedSolicita a emissão de **1 a 50 notas em uma única chamada**, de uma empresa ou de várias. Cada item tem os mesmos campos de POST /v1/invoices mais um idempotencyKey opcional. Os itens são conferidos um a um: os válidos são salvos juntos em uma única transação e os inválidos voltam como rejected com o motivo e **não** são salvos. A emissão continua assíncrona, como em POST /v1/invoices.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| invoices | array | ✓ | Lista de 1 a 50 itens. Cada item recebe os campos de POST /v1/invoices (companyId ou companyTaxId, service, amount, competenceDate, customer, discount, deduction, options opcionais...). Os itens podem ser de empresas diferentes da conta. Menos de 1 ou mais de 50 itens devolve 400 ERROR_VALIDATION para a requisição inteira. |
| invoices[].idempotencyKey | string | Chave de idempotência do item (até 255 caracteres). Com a mesma chave e o mesmo conteúdo, repetir o lote devolve a nota já salva com replayed: true em vez de emitir de novo. O header Idempotency-Key não é usado nesta rota. |
curl -X POST "$BASE_URL/invoices/batches" \
-H "X-API-Key: $CORVO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"invoices": [
{
"companyTaxId": "11222333000181",
"idempotencyKey": "planilha-out-linha-2",
"customer": { "name": "Cliente A", "taxId": "52998224725" },
"service": { "description": "Consultoria" },
"amount": 150.50,
"competenceDate": "2026-10-01"
},
{
"companyTaxId": "45723174000110",
"idempotencyKey": "planilha-out-linha-3",
"service": { "description": "Manutenção mensal" },
"amount": 300,
"competenceDate": "2026-10-01"
}
]
}'Resposta — 202 Accepted
{
"data": {
"total": 3,
"accepted": 2,
"rejected": 1,
"results": [
{
"index": 0,
"status": "accepted",
"invoice": {
"id": "6c4d2e81-0a3b-4c5d-9e7f-1a2b3c4d5e6f",
"companyId": "3f1c8a90-6b2e-4d5a-9c11-8e2f0a7b4d11",
"environment": "test",
"idempotencyKey": "planilha-out-linha-2",
"status": "queued",
"amount": 150.5,
"currency": "BRL",
"competenceDate": "2026-10-01T00:00:00.000Z",
"stage": "pending",
"createdAt": "2026-10-10T12:00:00.000Z"
}
},
{
"index": 1,
"status": "accepted",
"replayed": true,
"invoice": {
"id": "0b7a1f52-3c4d-4e6f-8a9b-2c3d4e5f6a7b",
"status": "queued",
"idempotencyKey": "planilha-out-linha-3",
"amount": 300
}
},
{
"index": 2,
"status": "rejected",
"error": {
"code": "ERROR_VALIDATION",
"message": "Request validation failed",
"details": [
{ "field": "amount", "code": "ERROR_OUT_OF_RANGE", "message": "amount must be greater than 0" }
]
}
}
]
}
}/v1/invoices/calculate200 OKCalcula os tributos de uma nota **sem emitir**: não grava nota, não consome crédito, não reserva número de DPS, não agenda nem chama a Sefin. Use para conferir a configuração fiscal e mostrar o valor ao cliente antes de POST /v1/invoices.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| (corpo) | objeto | ✓ | Mesmo corpo de POST /v1/invoices (empresa, service, amount, competenceDate, customer opcional, discount, deduction). Exige a permissão nfse.invoices.issue. Não exige certificado nem empresa pronta. |
curl -X POST "$BASE_URL/invoices/calculate" \
-H "X-API-Key: $CORVO_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"companyTaxId": "11222333000181",
"service": { "code": "060101", "description": "Corte de cabelo" },
"amount": 50,
"competenceDate": "2026-10-13"
}'Resposta — 200 OK
{
"data": {
"preEmission": true,
"status": "ready",
"disclaimer": "Pre-emission calculation. Values are not a fiscal document and may differ from the final validation by Sefin.",
"competenceDate": "2026-10-13",
"taxRegime": "MEI",
"classification": {
"serviceCode": "060101",
"source": "official_catalog",
"operationNature": "SERVICE",
"nbsCode": null,
"cst": null,
"classCode": null,
"serviceProfileId": null,
"catalogValidated": false
},
"applicability": {
"customerRequired": false,
"strategy": "mei",
"iss": {
"status": "NOT_APPLICABLE",
"reason": "ISS recolhido no DAS-MEI"
},
"federalWithholding": {
"status": "NOT_APPLICABLE",
"reason": "no federal withholding on payments to Simples Nacional/MEI optants (IN RFB 1.234/2012, art. 4, XI)"
},
"cbs": {
"status": "NOT_APPLICABLE",
"reason": "MEI não destaca IBS/CBS na fase de teste de 2026"
},
"ibs": {
"status": "NOT_APPLICABLE",
"reason": "MEI não destaca IBS/CBS na fase de teste de 2026"
}
},
"taxes": {},
"withholdings": [],
"netReceivableAmount": "50.00",
"collectionWaived": false,
"calculation": {
"source": "none",
"calculatorVersion": null,
"rulesVersion": "2026.10-r1",
"legalReferences": [
"Ato Conjunto RFB/CGIBS nº 4/2026; orientação CGNFS-e de 07/08/2026"
]
},
"warnings": [
{
"code": "CLASSIFICATION_NOT_CATALOG_VALIDATED",
"message": "Service tax classification was not validated against the official catalog",
"step": "ServiceTaxClassifier"
}
],
"pendingFields": [],
"blocking": null,
"trace": [
{
"step": "CompanyFiscalProfile",
"status": "applied",
"durationMs": 2.4
},
{
"step": "ServiceTaxClassifier",
"status": "applied",
"durationMs": 7.1
},
{
"step": "TaxApplicabilityResolver",
"status": "applied",
"durationMs": 0.3
},
{
"step": "RtcTaxCalculator",
"status": "skipped",
"durationMs": 0.03,
"reason": "MEI não destaca IBS/CBS na fase de teste de 2026"
},
{
"step": "FiscalValidator",
"status": "applied",
"durationMs": 0.1
}
]
}
}Notas agendadas
Use options.type: "SCHEDULE" com uma competenceDate futura para pedir que a nota seja emitida só naquele dia. A Corvo libera a emissão à 00:00 de America/Sao_Paulo da competenceDate. Até lá a nota fica com status: "queued" e scheduledFor traz o instante da liberação (ISO); em notas imediatas scheduledFor é null.
| Situação | Resultado |
|---|---|
Sem options.type (ou IMMEDIATE) e competenceDate futura | 422 com ERROR_FUTURE_DATE em details[].code. |
SCHEDULE e competenceDate futura até o limite | 202, status: queued, scheduledFor preenchido; a emissão acontece na data. |
SCHEDULE e competenceDate além do limite (padrão 15 dias à frente) | 422 com ERROR_SCHEDULE_HORIZON_EXCEEDED em details[].code. |
SCHEDULE e competenceDate de hoje ou passada | Emissão imediata, scheduledFor: null. |
Agendar uma nota
curl -X POST "$BASE_URL/invoices" \
-H "X-API-Key: $CORVO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido_123_agendada" \
-d '{
"companyTaxId": "11222333000181",
"service": { "description": "Corte de cabelo" },
"amount": 50,
"competenceDate": "2026-10-20",
"options": { "type": "SCHEDULE" }
}'Dedução da base do ISS
O emitente pode declarar em deduction um valor que a legislação do município permite abater da base de cálculo do ISS. O valor vai na DPS em vDedRed/vDR e reduz a base do ISS; a nota continua sendo emitida pelo valor total (amount).
| Campo / tributo | Como é tratado |
|---|---|
amount | Valor total da nota. |
deduction | Valor a abater da base do ISS. Maior que 0 e menor que amount menos discount; 0 equivale a omitir. |
| ISS | Base = amount − discount − deduction. |
| Retenções federais | Base inalterada: amount − discount. |
| IBS/CBS | Calculado sobre o valor sem a dedução. |
| Líquido a receber | Não muda com a dedução. |
| Erros de validação | 422 com out_of_range (negativa, ou não menor que amount menos discount) ou invalid_decimal_places em details[].code, no field: "deduction". |
| Resposta | A nota devolve deduction quando uma dedução foi declarada. |
Nota com dedução
curl -X POST "$BASE_URL/invoices" \
-H "X-API-Key: $CORVO_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: pedido_123" \
-d '{
"companyTaxId": "11222333000181",
"service": { "description": "Serviço de manutenção" },
"amount": 100,
"deduction": 40,
"competenceDate": "2026-10-20"
}'Schema Invoice
Cada nota tem estes campos:
| Campos | Tipo / observação |
|---|---|
id, companyId | Strings de identificação. |
environment | test ou live. |
externalId, idempotencyKey | String ou null. |
status | Enum descrito abaixo. |
customerSnapshot | taxId, name, email?, address?: { city, state, country? }. |
serviceSnapshot | description, code, municipalityCode. |
taxSnapshot | Objeto de informações fiscais. |
amount, currency, competenceDate | Number em reais, moeda e data-hora ISO 8601 (a data de competência à meia-noite UTC, ex.: 2026-10-01T00:00:00.000Z). |
discount, deduction | Numbers opcionais em reais: desconto incondicional e dedução da base do ISS declarada pelo emitente (veja Dedução da base do ISS). |
dpsId, dpsSeries, dpsNumber | String/string/number ou null. |
accessKey, nfseNumber | String/number ou null. |
fiscalProvider, fiscalSchemaVersion | Strings. |
issuedAt, authorizedAt | Data ISO ou null. |
emailSnapshot | Objeto opcional com send. |
lastErrorCode, lastErrorMessage, lastErrorProvider | String ou null. |
documentsReadyAt | Data ISO ou null. |
lastEmailStatus, lastEmailSentAt | queued, sent, failed ou null; data ISO ou null. |
documentsStatus | Estado derivado: pending ou ready. |
emailStatus | Estado derivado: queued, sent, failed ou null. |
stage | Estado derivado: pending, authorized, documents_ready, completed, failed. |
scheduledFor | Data ISO da liberação de uma nota agendada, ou null. |
cancellationReason, cancellationDescription | Motivo (issue_error, service_not_provided ou other) e justificativa do pedido de cancelamento, ou null. |
cancelledAt | Data ISO em que a nota chegou a cancelled, ou null. |
cancellationRequestedAt | Data ISO do pedido de cancelamento feito enquanto a nota estava processing, ou null. A nota é cancelada na Sefin assim que for autorizada. |
createdAt, updatedAt | Datas ISO. |
documentKeyVersion | Number opcional, quando presente no snapshot. |
requestHash é removido da serialização. O retorno atual usa documentsReadyAt/documentsStatus, e não o boolean documentsReady indicado por um DTO de Swagger. Não há objeto lastEmail no JSON atual; use os campos acima. stage: completed também pode ocorrer com email em failed; verifique emailStatus para saber se houve envio bem-sucedido.
Status fiscais: queued, processing, authorized, rejected, retry_pending, reconciliation_required, manual_review, failed, cancelling, cancelled. Não interprete autorização como disponibilidade imediata de todos os documentos.
/v1/invoices200 OKLista notas com filtros e paginação por cursor.
Query
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| companyId | string UUID | UUID da empresa. | |
| status | string | Um dos status fiscais: queued, processing, authorized, rejected, retry_pending, reconciliation_required, manual_review, failed, cancelling, cancelled. | |
| from, to | string ISO | Datas ISO; filtro inclusivo pela competência, não por createdAt. | |
| externalId | string | Correspondência exata. | |
| limit | integer | Inteiro entre 1 e 1000; padrão 50. | |
| cursor | string | nextCursor da página anterior, mantendo os mesmos filtros. |
curl "$BASE_URL/invoices?status=authorized&limit=50" \
-H "X-API-Key: $CORVO_API_KEY"Resposta — 200 OK
{
"data": [
{
"id": "c7d2e9a1-4b5f-4a6c-8d9e-0f1a2b3c4d5e",
"status": "authorized",
"amount": 150.5,
"createdAt": "2026-10-10T12:00:00.000Z"
}
],
"pagination": {
"limit": 50,
"hasMore": true,
"nextCursor": "eyJ2IjoxLCJjcmVhdGVkQXQiOiIyMDI2LTEwLTA5VDEyOjAwOjAwLjAwMDAwMFoiLCJpZCI6IjNmMWM4YTkwIn0"
}
}/v1/invoices/:id200 OKConsulta uma nota pelo id.
curl "$BASE_URL/invoices/$INVOICE_ID" \
-H "X-API-Key: $CORVO_API_KEY"Resposta — 200 OK
{
"data": {
"id": "c7d2e9a1-4b5f-4a6c-8d9e-0f1a2b3c4d5e",
"status": "authorized",
"accessKey": "352610123456789",
"nfseNumber": 123,
"documentsStatus": "ready"
}
}/v1/invoices/:id/xml200 OKBaixa o XML da nota autorizada.
curl "$BASE_URL/invoices/$INVOICE_ID/xml" \
-H "X-API-Key: $CORVO_API_KEY" --fail --output nfse.xml/v1/invoices/:id/pdf200 OKBaixa o PDF da nota autorizada.
curl "$BASE_URL/invoices/$INVOICE_ID/pdf" \
-H "X-API-Key: $CORVO_API_KEY" --fail --output nota.pdf/v1/invoices/:id/events200 OKConsulta o histórico de transições de status da nota, do mais antigo ao mais recente. Paginado como qualquer lista (limit, cursor).
curl "$BASE_URL/invoices/$INVOICE_ID/events" \
-H "X-API-Key: $CORVO_API_KEY"Resposta — 200 OK
{
"data": [
{
"id": "e1f2a3b4-0c5d-4e6f-8a9b-1c2d3e4f5a6b",
"invoiceId": "c7d2e9a1-4b5f-4a6c-8d9e-0f1a2b3c4d5e",
"fromStatus": "queued",
"toStatus": "authorized",
"reason": null,
"createdAt": "2026-10-10T12:01:00.000Z"
}
],
"pagination": {
"limit": 50,
"hasMore": false,
"nextCursor": null
}
}/v1/invoices/:id/download-url200 OKGera uma URL assinada e temporária para baixar o documento.
| Campo | Tipo | Obrigatório | Descrição |
|---|---|---|---|
| format | string | ✓ | "pdf" ou "xml". |
curl -X POST "$BASE_URL/invoices/$INVOICE_ID/download-url" \
-H "X-API-Key: $CORVO_API_KEY" \
-H "Content-Type: application/json" \
-d '{ "format": "pdf" }'Resposta — 200 OK
{
"data": {
"downloadUrl": "https://storage.example.com/documento-assinado",
"expiresAt": "2026-10-10T15:00:00.000Z",
"fileName": "nfse-123.pdf",
"mimeType": "application/pdf"
}
}/v1/invoices/:id/email202 AcceptedSolicita o envio de email com os documentos para o endereço armazenado em customerSnapshot.email.
curl -X POST "$BASE_URL/invoices/$INVOICE_ID/email" \
-H "X-API-Key: $CORVO_API_KEY" \
-H 'Idempotency-Key: pedido_123_email'Resposta — 202 Accepted
{
"data": {
"emailId": "pedido_123_email",
"status": "queued"
}
}/v1/invoices/:id/cancel200 OK / 202 AcceptedCancela uma nota. Uma nota queued (agendada ou ainda não processada) é cancelada na hora; uma NFS-e authorized passa a cancelling enquanto o evento 101101 é enviado à Sefin. Uma nota processing (em emissão) registra o pedido: 202, ainda processing, com cancellationRequestedAt preenchido.
curl -X POST "$BASE_URL/invoices/$INVOICE_ID/cancel" \
-H "X-API-Key: $CORVO_API_KEY" \
-H 'Content-Type: application/json' \
-d '{"reason":"issue_error","description":"Valor do serviço informado incorretamente"}'Resposta — 202 Accepted
{
"data": {
"id": "6f1c0e2a-4b7d-4c1e-9a55-2b8f7c3d9e10",
"status": "cancelling",
"stage": "completed",
"cancellationReason": "issue_error",
"cancellationDescription": "Valor do serviço informado incorretamente",
"cancelledAt": null,
"cancellationRequestedAt": null
}
}