DANFE Web

← Apresentação

API 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 Envelope de resposta Tipos de documento Formato do campo xml GET /api/health POST /api/logo POST /api/documents POST /api/documents/preview GET /api/documents/{access_key} GET /api/documents/{access_key}/pdf GET /api/documents/{access_key}/link GET & POST /api/config options.layout (retrato/paisagem) Accept: application/pdf Rota pública /nota.php Códigos de erro

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
}
CampoTipoDescrição
statusstring"success" ou "error"
messagestringmensagem legível (sempre em inglês)
dataobject | nullcorpo da resposta em sucesso; null em erro

Tipos de documento (campo type)

typeDocumentoChave de acessoObservação
nfeDANFE de NF-e (modelo 55)44 dígitosaceita options.layout
nfceDANFE de NFC-e (modelo 65)44 dígitosaceita options.layout
mdfeDAMDFE de MDF-e44 dígitossempre retrato (motor não lê layout)
nfseDANFSe de NFS-e50 dígitossomente 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:

FormatoExemplo
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
  }
}
CampoTipoDescrição
supported_typesstring[]valores aceitos no campo type
ffibooleantrue se o motor de geração de PDF interno da EXATA está ativo no servidor

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

CampoTipoDescrição
image_base64stringobrigatórioPNG, 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

CampoTipoDescrição
typestringobrigatórioum de nfe, nfce, mdfe, nfse
xmlstringobrigatóriover formato do XML
optionsobjectopcionalsobrepõe, campo a campo, os defaults salvos em /api/config — ver tabela abaixo

Campos de options (todos opcionais)

CampoTipoDefaultDescrição
layoutstringtpImp do XMLforça a orientação do DANFE — ver seção dedicada. Só nfe/nfce
logo_urlstring (URL)o salvo em danfe_logo_url de /api/configlogo impresso no cabeçalho do documento (baixado no momento do render)
footerstringo salvo em danfe_footertexto de rodapé (identificação de quem imprimiu, não do emitente da nota)
watermarkstringo salvo em danfe_watermarktexto de marca d'água sobre o documento
marginsobject{top:5,bottom:5,left:5,right:5}margens em milímetros — ver campos abaixo

options.margins, quando enviado, aceita qualquer subconjunto de:

CampoTipoDescrição
topnumbermargem superior, em mm
bottomnumbermargem inferior, em mm
leftnumbermargem esquerda, em mm
rightnumbermargem 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

CampoTipoDescrição
idintegeridentificador interno do documento nesta API — use nas outras rotas por chave (ou guarde para referência)
typestringeco do type enviado
access_keystringchave de acesso extraída do XML (44 ou 50 dígitos)
numberstringnúmero do documento
seriesstringsérie
environmentstring"producao" ou "homologacao", lido do tpAmb/tpAmb do XML
issuer.docstringCNPJ/CPF do emitente
issuer.namestringnome do emitente
recipient.docstringCNPJ/CPF do destinatário (vazio em MDF-e)
recipient.namestringnome do destinatário (vazio em MDF-e)
total_amountnumber | nullvalor total do documento
created_atstring (ISO 8601)quando este documento foi registrado por primeira vez nesta API
pdf_base64stringPDF codificado em base64
pdf_hexstringPDF codificado em hexadecimal (útil para quem consome de xHarbour/Clipper, que lida melhor com hex que com base64)
pdf_sizeintegertamanho do PDF em bytes (do binário, não do base64)
public_urlstring (URL)página pública HTML do documento (ver rota pública) — sem token, expira em 7 dias
pdf_urlstring (URL)mesmo link, mas devolve o PDF direto (Content-Disposition: inline) em vez da página HTML
download_urlstring (URL)mesmo link, forçando o download (Content-Disposition: attachment)
expires_atstring (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).

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.

CampoTipoO que é
product_namestringnome do produto na página pública (marca visual, não o emitente da nota)
logo_urlstring (URL)logo no cabeçalho da página pública
primary_colorstringuma de teal, indigo, violet, sky, emerald, amber, ou um hex #rrggbb
iconstringemoji ou URL de imagem para o ícone/favicon
danfe_logo_urlstring (URL)default de options.logo_url (logo dentro do PDF, diferente do logo_url acima, que é só da página)
danfe_footerstringdefault de options.footer
danfe_watermarkstringdefault de options.watermark
danfe_marginsobjectdefault 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.layoutEfeito
(campo omitido)respeita o <tpImp> do XML autorizado
retratoforça DANFE em pé
paisagemforça DANFE deitado
simplificadoforça DANFE simplificado
nfceforça layout de cupom
msg_eletronicaforç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âmetroDescrição
/<nome>.pdf (segmento de path, antes do ?)opcionalpuramente 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
idobrigatórioid interno do documento
expobrigatóriotimestamp Unix de expiração (faz parte da assinatura — não pode ser editado)
sigobrigatórioassinatura HMAC de id+exp — só quem tem o Bearer consegue gerar uma válida
rawopcional1 = devolve só o PDF, Content-Disposition: inline (abre no visualizador do navegador)
downloadopcional1 = 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

HTTPQuando
400corpo JSON inválido; parâmetros do link público incompletos
401sem token; token inválido; conta suspensa
404rota inexistente; documento não encontrado para a sua conta; link público de documento que não existe (mais)
405método HTTP não permitido naquela rota (ex: DELETE /api/config)
410link público expirado (mais de 7 dias) ou assinatura inválida
422validaçã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)
500falha interna ao gerar o PDF (XML corrompido) ou infraestrutura (banco de dados)
503banco 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.