Usamos cookies e tecnologias semelhantes, incluindo o Google Analytics e o Meta Pixel, para melhorar sua experiência, medir o tráfego do site e entender campanhas de cadastro. Ao clicar em "Aceitar", você concorda com esse uso.

Saiba mais sobre nossas práticas de privacidade
Corvo
English

Importar planilha

Emita notas em lote a partir de uma planilha CSV ou Excel: envio, revisão, emissão e acompanhamento dos contadores, por API ou MCP.

Emita centenas de notas a partir de uma planilha. Você envia um .csv ou .xlsx, a Corvo lê e confere cada linha em segundo plano, você revê os problemas se quiser, e então emite tudo. O mesmo fluxo existe no painel (Notas, Importar planilha) e para assistentes por MCP.

Como funciona

  • POST /v1/imports envia o arquivo e responde na hora com o id da importação.
  • Consulte GET /v1/imports/:id até o status ser ready. Cada linha é conferida sozinha: uma linha ruim nunca impede as outras.
  • Revisão opcional: GET /v1/imports/:id/rows?status=invalid mostra os problemas, PATCH /v1/imports/:id/rows/:line corrige um, GET /v1/imports/:id/errors.csv baixa só os erros.
  • POST /v1/imports/:id/emit emite as linhas válidas. Acompanhe os contadores até o status ser done.

A planilha

A primeira linha é o cabeçalho. Os nomes são reconhecidos sem diferenciar maiúsculas e acentos, em português ou inglês (valor ou amount). O CSV pode usar ; ou , e codificação UTF-8, UTF-16 ou do Windows. Os valores aceitam 1.234,56, 1234.56 e R$ 1.234,56; as datas aceitam dd/mm/aaaa (também 7/11/2026, 07-11-2026 e 07.11.2026) e aaaa-mm-dd. Colunas que não casam com nenhum nome abaixo são ignoradas e listadas em unknown_columns da importação. Fórmulas nunca são executadas: só o valor guardado é lido.

ColunaObrigatóriaO que é
cnpj_prestadorse não escolher a empresaCNPJ da empresa que emite a linha.
tomador_documentonãoCPF ou CNPJ do tomador, com ou sem pontuação. Zeros à esquerda perdidos pelo Excel são restaurados.
tomador_nomenãoNome do tomador.
tomador_emailnãoE-mail do tomador, para enviar a nota.
descricaosimDescrição do serviço.
valorsimValor em reais, com até 2 casas decimais.
codigo_serviconãoCódigo nacional do serviço (6 dígitos). Vazio usa o padrão da empresa.
data_competencianãoData de competência. Vazio usa hoje (America/Sao_Paulo).
pedidonãoNúmero do pedido ou da ordem de serviço impresso na nota (até 60 caracteres).
observacoesnãoTexto livre impresso na nota como informação complementar (até 2.000 caracteres).
tomador_nif, tomador_sem_nifnãoCliente no exterior: a identificação fiscal dele, ou o motivo de não ter (dispensado ou nao exigido). Entra no lugar de tomador_documento.
tomador_pais, tomador_codigo_postal, tomador_cidade, tomador_regiao, tomador_logradouro, tomador_numero, tomador_bairro, tomador_complementopara cliente no exteriorEndereço no exterior. O país é o código de duas letras (por exemplo US); todos são obrigatórios, menos o complemento. Essas colunas não vêm no modelo básico.

Regras

RegraDetalhe
LimitesAté 2.000 linhas e 10 MB por arquivo; só .csv ou .xlsx. Mais linhas falham a importação com ERROR_IMPORT_TOO_MANY_ROWS.
EmpresaDa coluna cnpj_prestador ou de companyId. Uma linha sem empresa fica invalid (ERROR_IMPORT_COMPANY_REQUIRED); um CNPJ diferente da empresa escolhida fica invalid (ERROR_IMPORT_COMPANY_MISMATCH).
AmbienteUma importação pertence ao ambiente da chave que a criou: uma chave test só vê importações de test, uma chave live só as de live.
IdempotênciaCada linha tem sua própria chave (import:<id>:<linha>), então repetir o emit ou refazer um job nunca emite uma linha duas vezes.
Não suportadoSCHEDULE e RECURRING não são aceitos dentro de uma planilha.
PermissõesLeitura (GET) exige invoices_view; enviar, corrigir, emitir e descartar exigem invoices_issue.

Status e contadores

statusSignificado
processingO arquivo está sendo lido e cada linha conferida.
readyLido. Dá para revisar, corrigir e emitir.
emittingEmitindo. Algumas notas podem ainda aguardar a prefeitura.
doneNada mais em andamento. Linhas aguardando créditos ainda podem ser emitidas de novo.
failedO arquivo não pôde ser lido (vazio, linhas demais ou não é uma planilha de verdade), ou a empresa escolhida não existe mais (ERROR_IMPORT_COMPANY_NOT_FOUND); veja o error_code.

counters: total, valid, invalid, accepted (salvas e na fila de emissão), authorized (a prefeitura já autorizou), awaiting_credits (recusadas por falta de créditos: **não são erro**), failed (linhas inválidas mais as notas que a prefeitura recusou), pending (ainda aguardando ou em andamento), valid_amount e awaiting_amount em reais. Durante a emissão, authorized_amount, pending_amount e failed_amount mostram em reais quanto já foi autorizado, quanto está em andamento e quanto foi recusado (linhas inválidas nunca entram, porque não chegaram a ser enviadas).

POST/v1/imports202 Accepted
full_accessinvoices_issueinvoices_read_write

Envia uma planilha (.csv ou .xlsx, até 2.000 linhas e 10 MB) como multipart/form-data. A leitura é **assíncrona**: a resposta é a importação a acompanhar, com status: "processing". Se este mesmo arquivo já foi enviado antes para a empresa e o ambiente, duplicate_of traz a importação anterior (um aviso, não um bloqueio).

Campos do formulário

CampoTipoObrigatórioDescrição
filefile✓A planilha .csv ou .xlsx.
companyIdstring (uuid)Empresa que emite as linhas sem CNPJ. Uma linha com o CNPJ de outra empresa é recusada, para que a empresa errada nunca emita.
curl -X POST "$BASE_URL/imports" \
  -H "X-API-Key: $CORVO_API_KEY" \
  -F "file=@notas-novembro.csv" \
  -F "companyId=5b0e7c5a-5d0b-4f45-8d6c-3a2a1e9d7f20"

Resposta — 202 Accepted: a importação a acompanhar

{ "data": { "id": "576905ae-1707-4848-9ed6-5a6e51800b74", "status": "processing", "duplicate_of": null } }
GET/v1/imports200 OK
full_accessinvoices_viewinvoices_read_write

Lista as importações recentes do ambiente, da mais nova para a mais antiga, com os contadores (no máximo 20). Útil para reencontrar uma importação, por exemplo uma que aguarda créditos.

Query

CampoTipoObrigatórioDescrição
limitintegerDe 1 a 20, padrão 10.
curl "$BASE_URL/imports?limit=10" \
  -H "X-API-Key: $CORVO_API_KEY"

Resposta — 200 OK: lista de importações

{ "data": [ /* imports, like GET /v1/imports/:id */ ] }
GET/v1/imports/:id200 OK
full_accessinvoices_viewinvoices_read_write

Status e contadores de uma importação. Consulte enquanto o status for processing ou emitting. Durante a leitura, rows_expected é quantas linhas o arquivo tem e counters.total quantas já foram conferidas.

curl "$BASE_URL/imports/576905ae-1707-4848-9ed6-5a6e51800b74" \
  -H "X-API-Key: $CORVO_API_KEY"

Resposta — 200 OK: a importação com os contadores

{
  "data": {
    "id": "576905ae-1707-4848-9ed6-5a6e51800b74",
    "status": "ready",
    "file_name": "notas-novembro.csv",
    "company_id": null,
    "error_code": null,
    "rows_expected": 300,
    "unknown_columns": [],
    "emit_started_at": null,
    "emit_finished_at": null,
    "created_at": "2026-11-07T13:05:21.000Z",
    "counters": {
      "total": 300,
      "valid": 297,
      "invalid": 3,
      "accepted": 0,
      "authorized": 0,
      "awaiting_credits": 0,
      "valid_amount": 718602.19,
      "awaiting_amount": 0,
      "failed": 3,
      "pending": 297,
      "authorized_amount": 0,
      "pending_amount": 718602.19,
      "failed_amount": 0
    }
  }
}
GET/v1/imports/:id/rows200 OK
full_accessinvoices_viewinvoices_read_write

As linhas de uma importação, **uma página por vez** (nunca a planilha inteira). Filtre por status para listar só as linhas com problema (invalid) e ler o motivo em error_code e error_message.

Query

CampoTipoObrigatórioDescrição
statusstringvalid, invalid, queued, accepted ou rejected.
limitintegerDe 1 a 200, padrão 50; acima disso responde 400.
cursorstringnextCursor da página anterior.
curl "$BASE_URL/imports/576905ae-1707-4848-9ed6-5a6e51800b74/rows?status=invalid&limit=50" \
  -H "X-API-Key: $CORVO_API_KEY"

Resposta — 200 OK: uma página de linhas

{ "data": [
  {
    "line": 42,
    "data": {
      "amount": "abc",
      "competenceDate": "2026-11-07",
      "companyTaxId": "61469487000140",
      "customer": { "name": "Maria da Silva", "taxId": "52998224725" },
      "service": { "description": "Consultoria mensal" }
    },
    "status": "invalid",
    "error_code": "ERROR_VALIDATION",
    "error_message": "Request validation failed",
    "invoice_id": null
  }
], "pagination": { "limit": 50, "hasMore": false, "nextCursor": null } }
PATCH/v1/imports/:id/rows/:line200 OK
full_accessinvoices_issueinvoices_read_write

Corrige uma linha e a confere de novo, somente enquanto a importação estiver ready. Envie os campos alterados em item. customer e service são mesclados campo a campo, então alterar o e-mail mantém o nome. companyId e companyTaxId não podem ser alterados. A resposta é a linha, valid ou ainda invalid com o motivo. Uma linha que já entrou na fila de emissão responde 409 com ERROR_IMPORT_STATE.

CampoTipoObrigatórioDescrição
itemobject✓Campos da nota a alterar: amount, competenceDate, customer { name, taxId, email }, service { description, code }.
curl -X PATCH "$BASE_URL/imports/576905ae-1707-4848-9ed6-5a6e51800b74/rows/42" \
  -H "X-API-Key: $CORVO_API_KEY" \
  -H 'Content-Type: application/json' \
  -d '{ "item": { "amount": 1500.5 } }'

Resposta — 200 OK: a linha, conferida de novo

{ "data": {
  "line": 42,
  "data": {
    "amount": 1500.5,
    "competenceDate": "2026-11-07",
    "companyTaxId": "61469487000140",
    "customer": { "name": "Maria da Silva", "taxId": "52998224725" },
    "service": { "description": "Consultoria mensal" }
  },
  "status": "valid",
  "error_code": null,
  "error_message": null,
  "invoice_id": null
} }
POST/v1/imports/:id/emit202 Accepted
full_accessinvoices_issueinvoices_read_write

Emite todas as linhas válidas, em pedaços de 50, com uma chave de idempotência por linha: chamar duas vezes nunca emite duas vezes. Se o limite do plano ou os créditos acabarem, as linhas restantes ficam como awaiting_credits (não são erro). Depois de recarregar, **chame de novo**: só essas linhas são enviadas.

curl -X POST "$BASE_URL/imports/576905ae-1707-4848-9ed6-5a6e51800b74/emit" \
  -H "X-API-Key: $CORVO_API_KEY"

Resposta — 202 Accepted: a importação, agora emitindo

{
  "data": {
    "id": "576905ae-1707-4848-9ed6-5a6e51800b74",
    "status": "emitting",
    "file_name": "notas-novembro.csv",
    "company_id": null,
    "error_code": null,
    "rows_expected": 300,
    "unknown_columns": [],
    "emit_started_at": null,
    "emit_finished_at": null,
    "created_at": "2026-11-07T13:05:21.000Z",
    "counters": {
      "total": 300,
      "valid": 297,
      "invalid": 3,
      "accepted": 0,
      "authorized": 0,
      "awaiting_credits": 0,
      "valid_amount": 718602.19,
      "awaiting_amount": 0,
      "failed": 3,
      "pending": 297,
      "authorized_amount": 0,
      "pending_amount": 718602.19,
      "failed_amount": 0
    }
  }
}

Outras rotas

MétodoRotaDescriçãoStatus
DELETE/v1/imports/:idDescarta uma importação que ainda não foi emitida (nada é emitido).204
GET/v1/imports/templateBaixa o modelo de planilha (CSV).200
GET/v1/imports/:id/errors.csvBaixa só as linhas que pedem atenção, em fluxo. As linhas que só aguardam créditos ficam de fora.200

Quando os créditos acabam

Ficar sem créditos não é falha da planilha. A emissão para, as linhas que não foram enviadas ficam como awaiting_credits (ERROR_MONTHLY_LIMIT_EXCEEDED, ERROR_INSUFFICIENT_CREDITS e códigos parecidos nas linhas) e nada se perde. Recarregue os créditos e chame POST /v1/imports/:id/emit de novo: só essas linhas são enviadas, com as mesmas chaves. Você reencontra a importação depois com GET /v1/imports.

Pelo MCP

Assistentes podem fazer o mesmo fluxo com as ferramentas import_create, import_list, import_get, import_rows, import_fix_row, import_emit e import_discard (veja a página de MCP). Uma planilha enviada por MCP é texto dentro da mensagem: CSV em content (ou um .xlsx em content_base64), até 1 MB; arquivos maiores vão por POST /v1/imports.