Antes deste documento convém ler a introdução: ela explica
por que o contrato usa o vocabulário do FIRE e não o do órgão fiscal.
1. Autenticação
São duas direções distintas e convém não confundi-las.1.1 Como se consome o Fiscal Gateway do FIRE
API key, e nada mais. É o único mecanismo, hoje e sempre. Não há OAuth, nem JWT de usuário, nem sessão.
A key é account + vendor scoped e precisa ter o scope
fiscal:write. O tenant é derivado da
key, nunca do corpo: um payload pode mentir, uma credencial não. Por isso a requisição não
leva accountId nem vendorId.
A Idempotency-Key é gerada por quem chama e reutilizada em cada retentativa da mesma venda.
Gerá-la nós seria idempotência decorativa: cada tentativa traria uma chave diferente e não haveria
nada a comparar.
Não é o que evita o documento duplicado — isso é papel do
orderCode, a chave natural
(país + orderCode + operação). Uma retentativa com o mesmo orderCode devolve o mesmo
documento mesmo que o canal regenere a chave, que é o erro de implementação mais comum.O que a chave acrescenta é detectar que foi reusada para outra venda: mesma chave com corpo
diferente responde 409 em vez de numerar.1.2 Como consumimos o provedor
Também API key. Mesmo mecanismo nas duas direções: o provedor entrega uma key por ambiente e o FIRE a envia emx-api-key a cada chamada. Não há OAuth, nem endpoint de token, nem audiência
para configurar.
A key fica guardada cifrada na configuração da conta e não sai da instância. É write-only no
backoffice: é carregada, nunca exibida.
2. Endpoint — um por país
{country} é o código ISO 3166-1 alpha-2 em minúsculas.
baseUrl e uma credencial; as rotas derivam do
país. O FIRE resolve o país antes de chamar —ele sai da loja— então não há nada a descobrir nem a
configurar à parte.
Por que por país e não uma rota única. Uma integração fiscal é construída e certificada
contra um órgão, e as normas mudam por país. Com uma rota por país, uma mudança no Equador é uma
versão do endpoint do Equador: não toca a Colômbia, não obriga a versionar tudo, e não pode
quebrá-lo. O versionamento fica com a mesma granularidade da mudança.Também torna desnecessário declarar capacidades: as rotas que existem são os países que você
atende.
3. Requisição
3.1 Numerar uma venda
Uma só requisição, igual para todos os países. Ela não muda de forma conforme o órgão: o que muda é o que cada provedor usa. O do Equador monta a chave de acesso com a data, o emitente e o sequencial, e nem olha os valores. O da Colômbia precisa de todos eles, porque o seu identificador é um hash da nota. Os dois exemplos abaixo são o mesmo contrato: mesmos campos, mesma ordem. A única coisa que muda são os valores.- Equador (EC)
- Colômbia (CO)
storeFiscalConfig.metadata vai vazio: no Equador o estabelecimento e o ponto de emissão você
resolve do seu lado, no seu catálogo, a partir de store.code e device.uid.3.2 Cancelar
Idêntico, com"operation": "CANCEL". Mesmos campos, client e totals incluídos: o
cancelamento emite um documento novo e precisa dos mesmos dados da emissão.
Não enviamos referência ao documento original. O provedor resolve o que compensa buscando a
emissão do mesmo orderCode — que é a sua própria chave de idempotência, já indexada.
3.3 Campos
Os campos vazios são omitidos. Nunca enviamos
"". Um campo ausente significa “não
configurado”; uma string vazia não deve ser interpretada como valor válido.
3.4 Os dois metadata
Há dois blocos chave-valor, em níveis distintos e com propósitos distintos:
store.storeFiscalConfig.metadata— atributos da loja, constantes. É onde vivem os dados de que o provedor precisa e que não fazem parte do domínio compartilhado: por exemplo a chave técnica e a faixa de numeração que a DIAN entrega com a resolução. São configurados uma vez no backoffice e trafegam em cada chamada daquela loja.metadata(raiz) — atributos da venda, variáveis: os que mudam a cada transação e que algum regime exige declarar.
Assim se carrega o da loja

Em cima: o NIT do emitente, a chave técnica e a faixa de faturamento como grupo aninhado.

Mais abaixo, na mesma tela: a faixa de notas de crédito e a prévia do JSON que será enviado.
store.storeFiscalConfig.metadata. Na
captura, aquela loja vai lhe mandar:
Por que é chave-valor e não um formulário com campos fixos.Os dados de que um provedor precisa são do regime do seu país, não do domínio que
compartilhamos: uma chave técnica da DIAN, uma faixa de numeração, o que vier depois. Tipificá-los
na nossa tela significaria que somar um país —ou um órgão acrescentar um requisito— obrigue a
fazer deploy do backoffice. Com chave-valor, é carregar uma linha.Os valores podem ser texto ou um grupo aninhado, sem limite de profundidade. Por isso uma
faixa inteira —prefixo, de, até, resolução, vigência— entra como um bloco, em vez de cinco chaves
com o prefixo colado ao nome.O exemplo leva duas faixas porque são duas coisas distintas: a de faturamento e a de notas de
crédito. Uma loja que só tenha a primeira pode emitir mas não cancelar.
Esse mesmo bloco também trafega nos eventos do pedido, não só na numeração. Aparece como
O detalhe campo a campo está em
data.store.storeFiscalConfig —com o seu metadata dentro— em:order.opened ·
order.completed ·
order.cancelled ·
order.invoiced ·
order.reversedÉ o mesmo dado nos dois caminhos, e de propósito: quem consome eventos para conciliar vê com que
configuração aquela venda foi emitida, sem ter de perguntar a ninguém.order.completed → Dados fiscais.Os nomes das chaves são definidos por você, não por nós. O campo é livre: quem configura a
loja escreve a chave que a sua integração espera. Por isso convém publicar quais você precisa e
com que nome exato — um
claveTecnica contra um clave_tecnica é um dado que chega e que você
não vai encontrar.O FIRE não valida esses nomes de propósito: o vocabulário é do regime e do provedor, e tipificá-lo
do nosso lado significaria fazer deploy do backoffice toda vez que um país novo pedir um dado
diferente.metadata pode sobrescrever um campo de domínio. Se aparecer uma chave
storeCode ou country dentro de metadata, ela deve ser ignorada. Caso contrário o chave-valor
vira a porta dos fundos por onde o contrato é redefinido.
3.5 client e totals — o que vem da venda
Estes dois blocos são os mesmos que o ponto de venda monta para injetar o pedido, e trafegam
tal como estão: o FIRE não os recorta nem os renomeia. Por isso não levam vocabulário fiscal —
levam o do negócio.
Vão sempre, em todos os países. O que muda é quem os usa: o provedor do Equador os ignora,
porque a chave de acesso é montada com data, emitente e sequencial. O da Colômbia precisa deles
inteiros, porque o CUFE é um hash da nota: entram os valores, cada imposto separadamente, a
data com hora e o documento do adquirente.
totals — o que foi cobrado
- Equador (EC)
- Colômbia (CO)
Moeda O SRI não os olha: a chave de acesso é montada com data, emitente e sequencial. Trafegam
mesmo assim, caso você precise deles para o seu próprio controle.
USD. Hoje, um único imposto: IVA a 15%.client — quem comprou
Chega inteiro, tal como o ponto de venda o montou. Não é um subconjunto fiscal: traz também
dados que não servem a nenhum órgão.
govIdType sai de um catálogo fechado. Estes são todos os valores que o FIRE emite, e não
vão chegar outros:Traduzi-los para o código que o seu órgão exige é parte da sua implementação, assim como o resto
da tradução para a língua do regime.
Leia só o que é fiscal e descarte o resto. O que um regime precisa está em
govIdType,
govIdNumber, name e —para empresas— billingInformation.businessName e
additionalInfo.fiscal. O uid, o e-mail e o telefone são do negócio, não do órgão.govIdType e govIdNumber aparecem duas vezes: na raiz e em billingInformation. Quando
divergem, vale o de faturamento — é o documento que o cliente pediu para a sua nota.- Equador (EC)
- Colômbia (CO)
FINAL_CONSUMER e zeros. Traduzi-lo para o que o SRI espera no
comprovante é parte da sua implementação.taxes traz sempre o detalhamento, um elemento por imposto, cada um com name, base,
rate e amount. O montante por imposto é o dado que importa: amount é o que você declara ao
órgão, e o taxValue de cima é apenas a soma deles.Percorra o array, não leia taxes[0]. Hoje no Equador e na Colômbia é um único IVA, mas um
regime pode declarar vários tributos por comprovante e o array os traz todos, sem que o contrato
mude.Os dois blocos trafegam nas duas operações,
INVOICE e CANCEL. É uma só requisição
canônica e não é recortada por operação.Não é simetria por capricho: o cancelamento produz um documento novo. Uma nota de crédito
colombiana tem o seu próprio identificador calculado sobre os valores e o adquirente, então sem
client e totals não haveria com o que montá-lo.O que não muda é o alcance: o cancelamento é total. Não existem cancelamentos parciais em
nenhum país que atendemos, então os valores que chegam são os da venda completa, e qual documento
você compensa se resolve pelo orderCode.4. Idempotência
A chave écountry + orderCode + operation.
Repetir essa tripla deve devolver o mesmo documento com "reused": true, sem consumir outro
sequencial. É a mesma chave que o FIRE usa do seu lado, para que um choque seja detectado nas duas
pontas ao mesmo tempo.
5. Resposta bem-sucedida
A resposta tem duas partes com regras distintas:- O envelope — idêntico em todos os países. É com ele que o FIRE opera: decide se tenta de novo, se houve comprovante, que erro reportar.
document— a língua fiscal do país. Cada um manda o que existe no seu regime, com os nomes do seu órgão, e nada mais.
document é a única coisa que muda, e por
isso aqui vai elidido: o seu conteúdo está em 5.2, com uma
seção por país. Se você está implementando o Equador, o bloco que lhe cabe é o do Equador e nenhum
outro.
country trafega mesmo estando na rota. Não é redundância: o FIRE compara country e
orderCode com o que pediu e descarta a resposta se não coincidirem. É o que evita imprimir
o documento de outra venda quando há um cruzamento de respostas ou um proxy com cache.5.1 status
Dois valores, um por operação:
Não há mais estados, e é deliberado. Este endpoint produz a representação fiscal —os
identificadores para imprimir— e nada mais. O envio ao órgão e a sua autorização ocorrem depois, do
lado do provedor, e o desfecho chega pelo callback. Modelar aqui estados de autorização mistura
dois ciclos de vida distintos.
status é quase um eco de operation, e existe por um único caso: quando a operação não produz
documento. O cancelamento no Brasil é um evento de cancelamento, não um documento novo, então a
resposta chega com document: null e sem graphic. Ali o status é a única coisa que afirma que a
operação foi concluída, em vez de deixar uma resposta bem-sucedida e vazia que não se distingue de
um erro silencioso.
Em particular:
- Não existe
PENDING. Imediatamente depois de numerar, o documento está sempre pendente de autorização: é a condição normal, não um estado a informar. O caixa imprime com os identificadores que acabou de receber. - Não existe
REJECTED. Se não foi possível numerar, é um erro: HTTP não-2xx com o blocofailure. Uma rejeição com200 OKe o motivo escondido num campo é um contrato onde alguém não valida e acredita ter numerado. - Não existe
REUSED. Isso éreused: true, um booleano ortogonal. É possível terINVOICEDcomreused: true— uma retentativa idempotente de uma venda já numerada — e essa distinção se perde seREUSEDfosse um estado.
5.2 document — o documento numerado
Aqui se fala a língua fiscal do país. É o único bloco da resposta que muda entre países, e muda
inteiro: os nomes são os do órgão, não uma tradução nossa.
Um país manda o que existe no seu regime e nada mais. Um campo que não se aplica não trafega em
null: simplesmente não está.
- Equador (EC) — SRI
- Colômbia (CO) — DIAN
As três peças continuam trafegando igual, como o SRI as nomeia e sem concatená-las numa
serie de 6 dígitos: elas servem para conciliar, não para compor o número.O puntoEmision que você devolver é o que ficou emitido, que pode não ser o que foi pedido em
device.uid através do seu catálogo. O que vale é sempre o que volta, nunca o que foi
mandado.Por que o bloco é do país e não um modelo comum
Avaliou-se um bloco plano com nomes por papel —accessKey, sequential, controlNumber— e ele foi
descartado. O custo não era um campo nulo: era que cada país novo acrescentava um campo que todos
os demais carregariam vazio para sempre, e que o mesmo identificador teria dois nomes conforme
entrasse pelo prekey ou pelo callback.
Este é, além disso, o mesmo mecanismo que o callback de resultado já usa, validando por país sobre o
countryCode da raiz. Um único padrão nas duas direções.
O que document NÃO leva
Não leva documentType. Com que instrumento fiscal a operação se materializa —uma nota de
crédito no Equador, um evento de cancelamento no Brasil— é assunto do país e do provedor. O que foi
pedido já é dito pelo status.
Não leva o que o órgão atribui ao autorizar — o numeroAutorizacion do SRI, o protocolo da
SEFAZ. Isso chega pelo callback; declará-lo aqui o condena a vir sempre em null.
Não leva authorizationMode nem issuedAt. São comuns a todos os países e vivem na raiz da
resposta.
5.3 graphic — o que é imprimível
Chave-valor, com as chaves de que cada país precisar. {} ou null quando a operação não
produz nada para imprimir — o cancelamento no Brasil, por exemplo.
qr, barcode, ted — não da simbologia do
momento.
Este bloco é o único da resposta que o provedor aporta e que não se pode derivar, por um caso
concreto: o QR da NFC-e brasileira é uma URL assinada com um hash que só o emitente consegue
construir. Não se deriva da chave. Se não chegar, não há QR.
No Equador o valor vai coincidir com document.claveAcceso. Essa redundância é deliberada: a
alternativa é o ponto de venda ter de saber que no Equador se codifica a chave, no Brasil a URL e no
Chile o TED.
Não inclui pdfUrl, xmlUrl nem lookupUrl. Os dois primeiros só existem depois de o órgão
autorizar e chegam pelo callback; declará-los aqui os condena a vir sempre em null, e um campo que
é sempre nulo ensina a ignorá-lo. lookupUrl é uma constante por país e ambiente, não um dado do
documento.
5.4 provider e metadata — os dois blocos do provedor
São dois blocos distintos e não intercambiáveis, e o FIRE os guarda em duas colunas diferentes.
A diferença é se o campo tem forma acordada ou não.
provider — identidade, com forma
Os três vão sempre presentes, com
null quando não se aplica. null diz “não tenho”; ausente
obriga a distinguir duas formas da mesma coisa.
reference não é a nossa Idempotency-Key. Aquela é mandada por nós e o provedor a ecoa por
outro lado. Esta é do provedor, e é a que serve quando é preciso escalar um caso a ele: sem ela, a
única forma de ele encontrar a operação é buscar por orderCode na faixa de datas certa.metadata — a sacola opaca
{}. O que não pode é mudar entre uma emissão e a sua retentativa idempotente: um 201
que traz a sacola populada e um 200 REUSED que a traz vazia descrevem a mesma operação de duas
maneiras diferentes, e quem ler o evento vai ver que “mudou” algo que não mudou.
6. Resposta com erro
Trafega com código HTTP não-2xx —422 para um problema de configuração ou de dados, 5xx para
um transitório. Nunca com 200.
retryable é obrigatório e quem decide é o provedor. É o que nos permite distinguir um problema
de configuração —que não melhora tentando de novo— de um transitório. Sem esse campo é preciso
adivinhar pelo código HTTP, e adivinhar errado significa tentar de novo no caixa enquanto o cliente
espera, ou abandonar uma venda que podia ser numerada.
failure.code deve ser um código estável e acionável, não um texto livre. É o que permite
construir alertas e documentação de suporte.
failure.message deve descrever o problema real, não uma generalidade. "identidad fiscal de tienda no configurada: EC / tienda K0050" permite consertar; "documento rejeitado" obriga a abrir
um chamado.
7. Regras da integração
O que enviamos manda. Se o catálogo do provedor tiver uma identidade fiscal diferente da que enviamos, ele deve rejeitar com erro explícito, nunca emitir com a sua. Um comprovante emitido sob o contribuinte errado não se conserta com um deploy.metadata é opaco nos dois sentidos e não pode sobrescrever campos de domínio.
Os códigos do órgão não trafegam no contrato. Nada de documentTypeCode: "01",
tipoComprobante ou equivalentes. O provedor os deriva de operation + country.
O contrato é versionado. Uma mudança quebrante requer uma versão nova do endpoint e uma janela de
convivência; não se muda o significado de um campo existente.
8. O callback de resultado
O callback de resultado segue como está. É correlacionado pororderCode + operation, com o
tenant derivado da API key com que se autentica. Deve incluir a operação: sem ela, uma nota e o seu
cancelamento sobre o mesmo pedido são indistinguíveis.
Devolver identificadores adicionais no callback é opcional e bem-vindo, mas não obrigatório.
Use os mesmos nomes da numeração. O callback da Colômbia declara
cufe, prefijo,
numeroDian, numeroComprobante, qrCode e ambiente — exatamente os de
5.2. É o mesmo documento contado duas vezes, e se os nomes
divergirem, conciliar os dois caminhos deixa de ser comparar campos e passa a ser traduzir, que é
onde os erros se infiltram.
