DANFE Web
← ApresentaçãoAPI de impressão de documento fiscal: recebe o XML autorizado de NF-e, NFC-e, MDF-e ou NFS-e (Padrão Nacional) e devolve o PDF do DANFE/DAMDFE/DANFSe — em base64, em hex, em binário, ou como link público pronto para o cliente final visualizar e baixar.
Índice
Autenticação
Todo endpoint /api/*, exceto /api/health, exige um token Bearer fornecido
pela EXATA. O token identifica a sua conta (software house) — documentos e configurações
ficam escopados a ele.
Authorization: Bearer <seu_token>
Token ausente, inválido ou de conta suspensa (inadimplência) → 401, sempre
com a mesma mensagem "Unauthorized or suspended account." (a API não distingue os dois
casos, por segurança).
Envelope de resposta
Toda resposta JSON (menos quando você pede o PDF binário, ver mais abaixo) vem neste formato fixo:
{
"status": "success" | "error",
"message": "descrição em inglês do que aconteceu",
"data": { ... } | null
}
| Campo | Tipo | Descrição |
|---|---|---|
status | string | "success" ou "error" |
message | string | mensagem legível (sempre em inglês) |
data | object | null | corpo da resposta em sucesso; null em erro |
Tipos de documento (campo type)
| type | Documento | Chave de acesso | Observação |
|---|---|---|---|
nfe | DANFE de NF-e (modelo 55) | 44 dígitos | aceita options.layout |
nfce | DANFE de NFC-e (modelo 65) | 44 dígitos | aceita options.layout |
mdfe | DAMDFE de MDF-e | 44 dígitos | sempre retrato (motor não lê layout) |
nfse | DANFSe de NFS-e | 50 dígitos | somente Padrão Nacional — layout municipal legado não é suportado |
Formato do campo xml
O campo xml (obrigatório em /api/documents e
/api/documents/preview) aceita três formatos, detectados automaticamente:
| Formato | Exemplo |
|---|---|
XML puro (começa com <) | "<?xml version=\"1.0\"?><nfeProc>...</nfeProc>" |
| base64 do XML | "PD94bWwgdmVyc2lvbj0iMS4wIj8+..." |
| data URI base64 | "data:text/xml;base64,PD94bWwg..." |
O XML precisa ser o documento já autorizado pela SEFAZ (com
<protNFe>/<protMDFe> ou equivalente) — esta API só
imprime, não emite nem assina.
GET /api/health GET
Diagnóstico. Não exige token.
curl https://danfe.seacweb.com.br/api/health
{
"status": "success",
"message": "danfe_web is up.",
"data": {
"service": "danfe_web",
"supported_types": ["nfe", "nfce", "mdfe", "nfse"],
"ffi": true
}
}
| Campo | Tipo | Descrição |
|---|---|---|
supported_types | string[] | valores aceitos no campo type |
ffi | boolean | true se o motor de geração de PDF interno da EXATA está ativo no servidor |
POST /api/logo POST
Grava o logo do seu cliente final (por tenant — cada token tem o seu, nunca
compartilhado entre clientes da mesma conta) e devolve uma URL pública pronta para usar em
options.logo_url. Esta API não aplica o logo automaticamente aos
próximos documentos — envie options.logo_url (com o valor devolvido aqui) em
cada POST /api/documents que precisar dele.
Corpo da requisição
| Campo | Tipo | Descrição | |
|---|---|---|---|
image_base64 | string | obrigatório | PNG, JPEG ou BMP, até 2 MB — base64 puro ou data URI (data:image/png;base64,...). BMP é convertido para PNG automaticamente antes de salvar. |
curl -X POST https://danfe.seacweb.com.br/api/logo \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{ "image_base64": "data:image/png;base64,iVBORw0KGgo..." }'
Resposta (HTTP 201)
{
"status": "success",
"message": "Logo saved successfully.",
"data": { "logo_url": "https://danfe.seacweb.com.br/logo.php?id=3f9a1c..." }
}
Reenviar (mesmo tenant) substitui o logo salvo e devolve uma URL nova — a URL
antiga para de funcionar (404). Erros: 422 imagem ausente/inválida/tipo
não suportado/maior que 2 MB.
POST /api/documents POST
Renderiza o PDF e registra o documento — gera o link público e permite
consultas/re-render posteriores por chave de acesso. Reenviar o mesmo xml (mesma chave de
acesso) atualiza o registro existente em vez de duplicar (o id e o link
continuam os mesmos, só a validação do link é renovada).
Corpo da requisição
| Campo | Tipo | Descrição | |
|---|---|---|---|
type | string | obrigatório | um de nfe, nfce, mdfe, nfse |
xml | string | obrigatório | ver formato do XML |
options | object | opcional | sobrepõe, campo a campo, os defaults salvos em /api/config — ver tabela abaixo |
Campos de options (todos opcionais)
| Campo | Tipo | Default | Descrição |
|---|---|---|---|
layout | string | tpImp do XML | força a orientação do DANFE — ver seção dedicada. Só nfe/nfce |
logo_url | string (URL) | o salvo em danfe_logo_url de /api/config | logo impresso no cabeçalho do documento (baixado no momento do render) |
footer | string | o salvo em danfe_footer | texto de rodapé (identificação de quem imprimiu, não do emitente da nota) |
watermark | string | o salvo em danfe_watermark | texto de marca d'água sobre o documento |
margins | object | {top:5,bottom:5,left:5,right:5} | margens em milímetros — ver campos abaixo |
options.margins, quando enviado, aceita qualquer subconjunto de:
| Campo | Tipo | Descrição |
|---|---|---|
top | number | margem superior, em mm |
bottom | number | margem inferior, em mm |
left | number | margem esquerda, em mm |
right | number | margem direita, em mm |
Exemplo completo
curl -X POST https://danfe.seacweb.com.br/api/documents \
-H "Authorization: Bearer <token>" \
-H "Content-Type: application/json" \
-d '{
"type": "nfe",
"xml": "<XML autorizado, texto puro ou base64>",
"options": {
"layout": "retrato",
"logo_url": "https://.../logo.png",
"footer": "Impresso por Minha Software House",
"watermark": "",
"margins": { "top": 5, "bottom": 5, "left": 5, "right": 5 }
}
}'
Resposta (HTTP 201)
{
"status": "success",
"message": "Document rendered successfully.",
"data": {
"id": 4823,
"type": "nfe",
"access_key": "35250912345678000190550010000012341123456789",
"number": "1234",
"series": "1",
"environment": "producao",
"issuer": { "doc": "12345678000190", "name": "EMPRESA EXEMPLO LTDA" },
"recipient": { "doc": "98765432000110", "name": "CLIENTE Y" },
"total_amount": 1540.00,
"created_at": "2026-09-11T14:31:00-03:00",
"pdf_base64": "JVBERi0xLjc...",
"pdf_hex": "255044462d312e37...",
"pdf_size": 48213,
"public_url": "https://danfe.seacweb.com.br/nota.php/nfe_352509...582.pdf?id=4823&exp=1789...&sig=ab34...",
"pdf_url": "https://danfe.seacweb.com.br/nota.php/nfe_352509...582.pdf?id=4823&exp=1789...&sig=ab34...&raw=1",
"download_url": "https://danfe.seacweb.com.br/nota.php/nfe_352509...582.pdf?id=4823&exp=1789...&sig=ab34...&download=1",
"expires_at": "2026-09-18T14:31:00-03:00"
}
}
Campos da resposta
| Campo | Tipo | Descrição |
|---|---|---|
id | integer | identificador interno do documento nesta API — use nas outras rotas por chave (ou guarde para referência) |
type | string | eco do type enviado |
access_key | string | chave de acesso extraída do XML (44 ou 50 dígitos) |
number | string | número do documento |
series | string | série |
environment | string | "producao" ou "homologacao", lido do tpAmb/tpAmb do XML |
issuer.doc | string | CNPJ/CPF do emitente |
issuer.name | string | nome do emitente |
recipient.doc | string | CNPJ/CPF do destinatário (vazio em MDF-e) |
recipient.name | string | nome do destinatário (vazio em MDF-e) |
total_amount | number | null | valor total do documento |
created_at | string (ISO 8601) | quando este documento foi registrado por primeira vez nesta API |
pdf_base64 | string | PDF codificado em base64 |
pdf_hex | string | PDF codificado em hexadecimal (útil para quem consome de xHarbour/Clipper, que lida melhor com hex que com base64) |
pdf_size | integer | tamanho do PDF em bytes (do binário, não do base64) |
public_url | string (URL) | página pública HTML do documento (ver rota pública) — sem token, expira em 7 dias |
pdf_url | string (URL) | mesmo link, mas devolve o PDF direto (Content-Disposition: inline) em vez da página HTML |
download_url | string (URL) | mesmo link, forçando o download (Content-Disposition: attachment) |
expires_at | string (ISO 8601) | quando public_url/pdf_url/download_url (estes três, gerados nesta chamada) deixam de funcionar |
Quer o PDF sem o envelope JSON? Veja Accept: application/pdf.
POST /api/documents/preview POST
Mesmo corpo e mesmas options de POST /api/documents (acima) —
mas não registra nada: sem id, sem link público, sem
histórico. Útil para testar layout/options antes de gerar de verdade.
curl -X POST https://danfe.seacweb.com.br/api/documents/preview \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{ "type": "nfe", "xml": "..." }'
Resposta (HTTP 200) — os mesmos campos de POST /api/documents, exceto
id, created_at, public_url, pdf_url,
download_url e expires_at (não existem, pois nada foi registrado):
{
"status": "success",
"message": "Preview generated successfully.",
"data": {
"type": "nfe", "access_key": "...", "number": "...", "series": "...",
"environment": "producao", "issuer": {...}, "recipient": {...},
"total_amount": 1540.00,
"pdf_base64": "...", "pdf_hex": "...", "pdf_size": 48213
}
}
GET /api/documents/{access_key} GET
Metadados de um documento já registrado — não re-renderiza (sem
campos pdf_*). {access_key} é a chave de acesso (44 ou 50 dígitos;
pontuação/espaços, se enviados, são ignorados).
curl https://danfe.seacweb.com.br/api/documents/35250912345678000190550010000012341123456789 \ -H "Authorization: Bearer <token>"
{
"status": "success",
"message": "Document retrieved successfully.",
"data": {
"id": 4823, "type": "nfe", "access_key": "...", "number": "...", "series": "...",
"environment": "producao", "issuer": {...}, "recipient": {...}, "total_amount": 1540.00,
"created_at": "2026-09-11T14:31:00-03:00",
"public_url": "...", "pdf_url": "...", "download_url": "...", "expires_at": "..."
}
}
404 se a chave não existe para a sua conta (documentos de outra
conta nunca são encontrados, mesmo que a chave exista no sistema).
GET /api/documents/{access_key}/pdf GET
Re-renderiza o PDF a partir do XML guardado (com os options salvos no momento do
registro original) e devolve um link novo, válido por mais 7 dias.
curl https://danfe.seacweb.com.br/api/documents/35250912345678000190550010000012341123456789/pdf \ -H "Authorization: Bearer <token>"
Resposta: os mesmos campos de GET /api/documents/{access_key} mais
pdf_base64, pdf_hex, pdf_size. Também aceita
Accept: application/pdf (ver abaixo).
GET /api/documents/{access_key}/link GET
Gera um link público novo (validade 7 dias a partir de agora), sem re-renderizar o PDF nem gastar tempo de CPU do servidor. Use para renovar um link que expirou.
curl https://danfe.seacweb.com.br/api/documents/35250912345678000190550010000012341123456789/link \ -H "Authorization: Bearer <token>"
{
"status": "success",
"message": "Link generated successfully.",
"data": {
"public_url": "...", "pdf_url": "...", "download_url": "...", "expires_at": "2026-09-18T14:40:00-03:00"
}
}
GET & POST /api/config
Lê e grava, para a sua conta (a revenda por trás do seu token), a marca
exibida na página pública (/nota.php) e os defaults de
options usados quando você não os informa numa chamada específica.
GET /api/config GET
curl https://danfe.seacweb.com.br/api/config -H "Authorization: Bearer <token>"
{
"status": "success",
"message": "Config retrieved successfully.",
"data": {
"product_name": "Minha Nota",
"logo_url": "https://.../logo.png",
"primary_color": "indigo",
"icon": "",
"danfe_logo_url": "https://.../danfe-logo.png",
"danfe_footer": "Impresso por Minha Software House",
"danfe_watermark": "",
"danfe_margins": { "top": 5, "bottom": 5, "left": 5, "right": 5 }
}
}
POST /api/config POST
Envie qualquer subconjunto dos campos abaixo — só os enviados são alterados.
| Campo | Tipo | O que é |
|---|---|---|
product_name | string | nome do produto na página pública (marca visual, não o emitente da nota) |
logo_url | string (URL) | logo no cabeçalho da página pública |
primary_color | string | uma de teal, indigo, violet, sky, emerald, amber, ou um hex #rrggbb |
icon | string | emoji ou URL de imagem para o ícone/favicon |
danfe_logo_url | string (URL) | default de options.logo_url (logo dentro do PDF, diferente do logo_url acima, que é só da página) |
danfe_footer | string | default de options.footer |
danfe_watermark | string | default de options.watermark |
danfe_margins | object | default de options.margins (campos top/bottom/left/right, em mm) |
curl -X POST https://danfe.seacweb.com.br/api/config \
-H "Authorization: Bearer <token>" -H "Content-Type: application/json" \
-d '{ "product_name": "Minha Nota", "primary_color": "indigo", "danfe_footer": "Emitido via Minha Nota" }'
Resposta: os mesmos campos de GET /api/config, já atualizados.
options.layout — forçar retrato/paisagem (só NFe/NFC-e)
Por padrão, a orientação do DANFE é a que o emissor da
nota já definiu no XML (campo <tpImp>). Envie
options.layout para forçar uma orientação diferente
só naquela renderização — o XML original, guardado para re-render/link
público, nunca é alterado:
| options.layout | Efeito |
|---|---|
| (campo omitido) | respeita o <tpImp> do XML autorizado |
retrato | força DANFE em pé |
paisagem | força DANFE deitado |
simplificado | força DANFE simplificado |
nfce | força layout de cupom |
msg_eletronica | força layout de mensagem eletrônica |
mdfe (o motor sempre gera retrato) e nfse (Padrão Nacional
não tem esse conceito) ignoram options.layout silenciosamente — nenhum erro,
o documento sai como sempre sai.
Accept: application/pdf — PDF cru, sem envelope JSON
Em POST /api/documents, POST /api/documents/preview e
GET /api/documents/{access_key}/pdf, envie o header abaixo para receber os
bytes do PDF diretamente (sem base64, sem JSON):
curl -X POST https://danfe.seacweb.com.br/api/documents \
-H "Authorization: Bearer <token>" \
-H "Accept: application/pdf" \
-H "Content-Type: application/json" \
-d '{ "type": "nfe", "xml": "..." }' \
-o nota.pdf
A resposta vem com Content-Type: application/pdf e
Content-Disposition: inline; filename="nfe_<chave>.pdf". Em
POST /api/documents o documento é registrado normalmente (o link público
existe, só não vem nesta resposta) — consulte depois com
GET /api/documents/{access_key} se precisar dele.
Rota pública /nota.php
É o link que public_url/pdf_url/download_url
apontam — sem token, para enviar direto ao destinatário, contador ou
cliente final. Formato completo:
https://danfe.seacweb.com.br/nota.php[/<nome-de-arquivo>.pdf]?id=<id>&exp=<timestamp>&sig=<assinatura>[&raw=1|&download=1]
| Parâmetro | Descrição | |
|---|---|---|
/<nome>.pdf (segmento de path, antes do ?) | opcional | puramente decorativo — faz a aba do navegador mostrar um nome de arquivo em vez de "nota.php". Pode ser omitido ou qualquer texto; a API sempre gera com o nome certo |
id | obrigatório | id interno do documento |
exp | obrigatório | timestamp Unix de expiração (faz parte da assinatura — não pode ser editado) |
sig | obrigatório | assinatura HMAC de id+exp — só quem tem o Bearer consegue gerar uma válida |
raw | opcional | 1 = devolve só o PDF, Content-Disposition: inline (abre no visualizador do navegador) |
download | opcional | 1 = devolve só o PDF, Content-Disposition: attachment (força a caixa de "Salvar como") |
Sem raw nem download: página HTML com resumo do documento
(valor, emitente, destinatário, número, chave de acesso com botão de copiar),
botões "Baixar PDF" e "Abrir", e um botão "Consultar na SEFAZ" (que copia a chave de
acesso para a área de transferência antes de abrir o portal — o portal nacional
exige colar a chave manualmente, não aceita preenchimento automático por URL, por
segurança). O PDF é montado em tempo real a cada acesso a partir do XML
guardado — nada fica salvo em disco entre um acesso e outro.
id/exp/sig ausentes ou incompletos → 400.
Link expirado (mais de 7 dias desde que foi gerado) ou assinatura inválida → 410
— gere um novo com GET /api/documents/{access_key}/link.
Documento inexistente → 404.
Códigos de erro
| HTTP | Quando |
|---|---|
| 400 | corpo JSON inválido; parâmetros do link público incompletos |
| 401 | sem token; token inválido; conta suspensa |
| 404 | rota inexistente; documento não encontrado para a sua conta; link público de documento que não existe (mais) |
| 405 | método HTTP não permitido naquela rota (ex: DELETE /api/config) |
| 410 | link público expirado (mais de 7 dias) ou assinatura inválida |
| 422 | validação: type ausente/inválido; xml ausente/ilegível; options.layout com valor fora da lista; chave de acesso lida do XML com tamanho errado (diferente de 44 ou 50 dígitos, conforme o tipo) |
| 500 | falha interna ao gerar o PDF (XML corrompido) ou infraestrutura (banco de dados) |
| 503 | banco de dados central temporariamente indisponível |
Em erro, o envelope é sempre { "status": "error", "message": "...", "data": null }
(menos nas rotas que devolvem PDF binário, onde um erro sempre volta como JSON normal, nunca
como PDF corrompido).
Dúvidas ou acesso: fale com a EXATA Sistemas.