Emissão Conjunta POST
Gera um link web de uso único onde o tomador (ou qualquer pessoa sem acesso à sua API) revisa, completa e confirma os dados de uma NFS-e antes da emissão de verdade -- sem precisar de API key, login ou acesso ao seu sistema.
Quando utilizar
Quando quem tem os dados finais do serviço não é o seu backend -- ex.: o tomador precisa confirmar/completar a descrição do serviço, escolher o código NBS/CNAE certo, ou aprovar o valor antes da nota sair. Você cria a sessão pela API (com o que já sabe: prestador, tomador, serviço, valor, alíquota) e manda o link -- a outra pessoa faz o resto no navegador.
codigo_nbs na criação da
sessão -- a tela de revisão já abre com ele preenchido, e quem revisar não precisa
escolher manualmente. Sem esse campo, a busca de NBS fica em branco até a pessoa
preencher na hora. É obrigatório pra confirmar a sessão, mesmo que não
seja exigido nesta chamada de criação.
reforma_tributaria.cst/
classificacao_tributaria) nesse caso. Se você não souber a classificação
específica, não precisa mandar nada aqui -- a API preenche automaticamente com a
classificação genérica (tributação integral) na hora de confirmar.
POST /v1/joint-emissions/sessions
só cria a sessão (o rascunho + o link) -- a emissão de fato só acontece quando alguém
abre o link e confirma na tela, ou usa POST .../confirm dentro da própria sessão
(endpoints de token, não fazem parte desta API Bearer). Se você quer emitir direto, sem essa
etapa de revisão humana, use POST /v1/nfse/emissoes.
Pré-requisitos
- Contrato com o produto Emissão Conjunta ativo (separado do produto NFS-e padrão).
- Prestador cadastrado, ativo, com certificado digital -- mesmos requisitos de emissão direta.
- Tomador pré-carregado via
POST /v1/contribuintes/{id}/tomadores, se quiser que a tela já abra com os dados dele preenchidos. Sem isso, a sessão ainda é criada, mas quem abrir o link precisa digitar os dados do tomador manualmente.
Fluxo da operação
Pra saber o resultado, duas opções: consultar GET /v1/joint-emissions/sessions/{session_id}
(Bearer) depois, ou informar callback_url na criação -- a CentralFiscal faz um
POST pra essa URL quando o tomador confirma (status vira
authorized ou rejected). É best-effort: uma
tentativa só, sem retry se sua URL estiver fora do ar, e sem assinatura/HMAC no corpo -- não
é (ainda) um sistema de webhooks genérico, é específico desta sessão. Pra confiabilidade,
trate o callback como um aviso e confirme sempre via GET.
O link (emissionUrl) é de uso único -- vale só pra essa sessão/documento.
Se o mesmo prestador for usar a Emissão Conjunta com frequência, a resposta também traz um
workspaceUrl fixo (uma espécie de "CRM de emissão" -- lista de sessões + criar
documento novo, sem precisar gerar link a cada vez). O workspaceUrl só
aparece no corpo da resposta na primeira vez que é criado pra aquele prestador --
mesma regra de qualquer segredo neste produto (ex.: API key): guarde-o, ele não volta a
aparecer em chamadas seguintes.
Endpoint
Headers
| Header | Valor | |
|---|---|---|
Authorization | Bearer <sua_api_key> | obrigatório |
Content-Type | application/json | obrigatório |
Isso vale só pra esta chamada (criar a sessão). O link resultante
(emissionUrl/workspaceUrl) que a outra pessoa abre no navegador
não usa API key -- a segurança dali é o próprio token opaco na URL, de uso
único e com expiração.
Body
Clique num campo com ▸ pra expandir.
POST .../tomadores, a tela abre com os dados dele preenchidos -- senão, quem abrir o link precisa digitar.2 para 2%. Dispensado se o prestador for Simples Nacional/MEI e o ISS não for retido pelo tomador -- nesse caso o ISS é recolhido dentro do DAS, e o campo relevante é percentual_total_tributos_simples abaixo, não este. Obrigatório em todos os demais casos (regime normal, ou Simples/MEI com retenção pelo tomador).pTotTribSN, e não temos como calculá-lo). Sem relação com aliquota_iss acima. Não envie 0 como placeholder -- é tratado como valor real, e SEFIN autoriza a nota com um percentual de tributos que não existe.cst=000/classificacao_tributaria=000001, tributação integral) na hora de confirmar. Mande explicitamente só se souber que o serviço tem uma classificação diferente da genérica.rps_numero, usa a numeração nacional padrão.codigo_servico/descricao_servico soltos na raiz -- mesmo formato do grupo servico da emissão direta de NFS-e. Único objeto aninhado de fato lido; os demais (prestador/tomador completos) não são.POST best-effort (1 tentativa, sem retry, sem assinatura) quando o tomador confirma. Não confunda com um sistema de webhooks genérico -- é específico desta sessão.payload_inicial ou
similar).
400, com a lista do que foi rejeitado e do que é
aceito. Não mande variações de nome "pra garantir" (ex.: nbs, nbsCode
e codigo_nbs juntos) -- use exatamente um dos nomes documentados aqui; objetos
aninhados como prestador/tomador completos também não são lidos, só
os campos de nível raiz acima.
Gap conhecido
Compra governamental (grupo gTribCompraGov do XSD de IBS/CBS)
ainda não é suportado pela Emissão Conjunta nem pela emissão direta -- se o tomador for órgão
público com esse regime específico, a CentralFiscal ainda não tem como marcar isso no
documento. Sem previsão nesta versão da API.
Exemplos por regime tributário
Os campos obrigatórios mudam de verdade conforme o regime do prestador -- aliquota_iss,
percentual_total_tributos_simples e reforma_tributaria nunca são os três
juntos ao mesmo tempo. Quatro exemplos completos: um "máximo" com todos os campos, e mais três
-- um por regime -- veja o código ao lado.
Máximo (todos os campos)
Mandar todo campo documentado de uma vez é seguro, mesmo os que não valem pro regime do seu
prestador. Quem decide o que de fato entra no XML é o regime tributário já cadastrado
no contribuinte (optante_simples_nacional/MEI, resolvido internamente),
nunca o que você manda: aliquota_iss é aceito e usado quando enviado mesmo se
dispensado pro seu regime; percentual_total_tributos_simples só é lido de verdade
pra prestador ME/EPP -- pra MEI e regime normal, o motor nem olha esse campo (usa
indTotTrib/vTotTrib em vez disso); reforma_tributaria
é usado sempre que presente, e auto-preenchido pela API quando ausente e o regime exigir
(não-Simples/MEI). Se você não quer decidir campo a campo por integração, pode simplesmente
mandar tudo que souber -- não há risco de rejeição por excesso de informação.
MEI
ISS recolhido dentro do DAS (não destacado) -- aliquota_iss dispensado.
percentual_total_tributos_simples não se aplica a MEI (só a ME/EPP). IBS/CBS
(reforma_tributaria) também dispensado -- MEI recolhe por fora, sem destacar na DPS.
Simples Nacional (ME/EPP, não-MEI)
Mesma dispensa de aliquota_iss do MEI (ISS recolhido no DAS), mas aqui
percentual_total_tributos_simples é obrigatório -- o Ambiente
Nacional exige esse percentual explícito (pTotTribSN) pra ME/EPP, e não tem como
calculá-lo. reforma_tributaria continua dispensado (mesma razão do MEI).
Regime normal
Aqui aliquota_iss é obrigatório (não há DAS que recolha por
fora). percentual_total_tributos_simples não se aplica (só é exigido pra
Simples/ME-EPP). reforma_tributaria não foi enviado neste exemplo de propósito --
o Ambiente Nacional exige a classificação de IBS/CBS pra regime normal, e como não foi
informada aqui, a API preenche sozinha a classificação genérica (tributação integral) na hora
de confirmar. Mande o campo explicitamente se souber a classificação real do serviço.
Resposta de sucesso
200 OK com o link pronto pra enviar. status começa como
"pending_confirmation" -- ainda não foi emitido, só a sessão foi criada.
Próximos passos
- Enviar
emissionUrlpro tomador (e-mail, link direto, o que fizer sentido pro seu fluxo). - Consultar
GET /v1/joint-emissions/sessions/{session_id}pra saber se já foi confirmado/emitido -- ou usarcallback_urlpra ser avisado. - Listar sessões de um prestador:
GET /v1/joint-emissions/sessions?prestador_documento=.... - Pré-carregar tomadores (recomendado):
POST /v1/contribuintes/{id}/tomadores.