POST /v1/indirects. O modelo direto e indireto explica onde fica o dinheiro de um participante indireto: a conta de liquidação no Banco Central, o limite na JD e a posição no seu ledger.
Esta página explica o que uma participação hospedada é e como ela se comporta. Hospedar participantes indiretos trata do provisionamento: a chave de criptografia do segredo de entrega, a postura de hospedagem e a chamada de cadastro. A numeração dela continua a partir da página de configuração. Desses passos, apenas a chave de criptografia recusa todo cadastro sozinha, então provisione-a primeiro. A postura de hospedagem controla os caminhos do dinheiro, não o registro, então você pode cadastrar participantes antes de ativá-la.
Quem é responsável pelo quê
A maioria dos erros operacionais aqui vem de procurar a resposta na coluna errada.
Na prática, um Pix para um cliente de um participante indireto para com você. Ele cai em uma conta que representa a posição daquela instituição. Você não sabe, e não pode saber, qual dos clientes dela é o dono do dinheiro. O participante indireto credita o cliente dele no próprio core, depois de receber o seu aviso.
O que cadastrar um indireto cria
Um cadastro grava uma linha, e o plugin também cria a posição de liquidação dessa instituição no Midaz. Essa é uma conta do ledger cujo alias começa com
@pi_ e termina com o ISPB da instituição, o código de 8 letras maiúsculas ou dígitos que o BACEN dá a cada participante. Um indireto no ISPB 12345678 liquida em @pi_12345678.
Essa posição guarda o dinheiro dessa instituição. A resposta a retorna como piAccountAlias. O plugin deriva o alias de ispb. Você nunca o escolhe, e um cliente nunca cria a conta.
Cada instituição tem uma posição. Um crédito para um indireto cai apenas na conta desse indireto. Por isso o dinheiro de uma instituição nunca se mistura com o de outra, nem com o seu. O registro existe para dar essa segregação.
O alias usa underscore, não barra:
@pi_12345678. O Midaz rejeita barra em um alias de usuário, então um nome no formato de caminho não resolve a nada.Antes de cadastrar alguém
A participação indireta vem desligada por padrão. A flag do systemplane por tenant
plugin-br-pix-jd.indirects/enabled deve estar ligada, e o valor padrão dela é false.
A flag controla duas metades, e elas se comportam de formas diferentes:
- A API de gerenciamento funciona com a flag desligada. Você pode cadastrar participantes, listá-los e ler um deles antes da habilitação. O onboarding antes do go-live é proposital.
- Nenhum dinheiro se move com a flag desligada. A resolução decide a qual indireto um crédito recebido pertence. Com a flag desligada, ela reporta a funcionalidade como desabilitada. Nenhum crédito recebido vai para uma conta
@pi, e nenhum indireto pode originar uma ordem de saída.
422 PIX-0111. Uma é a escrita de certificado de QR próprio em PATCH /v1/indirects/{indirectId}. A outra é GET /v1/indirects/{indirectId}/jwks.
A leitura falha fechada. Quando um caminho do dinheiro “volta ao comportamento de direto” e ninguém mudou a chave, essa é a causa. Olhe o systemplane.
Cadastrando um indireto
1
Ative a funcionalidade para o tenant
Defina
plugin-br-pix-jd.indirects/enabled como true. O cadastro funciona sem isso, mas nada liquida até que esteja ligada.2
POST /v1/indirects
Envie o
name da instituição, o ispb dela, e um bloco delivery com endpointUrl e secret. A resposta é 201. Ela traz indirectId, o identificador pelo qual toda chamada posterior é roteada, e piAccountAlias.3
Nada para consultar em loop: um 201 significa pronto
Toda checagem roda antes que o plugin grave a linha do registro. Primeiro vem a verificação de unicidade do ISPB, depois a verificação opcional no diretório da JD, depois a criação da conta
@pi_{ispb} no Midaz. Apenas então o plugin grava a linha, com status ACTIVE. Não há estado intermediário nem nada a esperar.4
Em caso de falha, corrija a causa e envie o POST de novo
Uma etapa que falha responde com um erro codificado que nomeia a etapa. O plugin não grava nenhuma linha do registro, então o ISPB continua livre. Corrija a causa e envie o POST de novo. Essa é a recuperação. Não há uma rota de nova tentativa separada, porque não resta nenhuma linha parcialmente escrita para retomar.
201 significa pronto, e uma falha não deixa nenhuma linha no registro. Em um 201, o status é sempre ACTIVE. Não escreva um cliente que fica consultando em loop até o participante ficar roteável. Esse loop espera por uma mudança que não pode acontecer.
Uma falha pode deixar para trás apenas a conta de liquidação, quando essa etapa teve sucesso e uma posterior falhou. O plugin não a desfaz, e ela não causa dano. Nada é roteado para uma conta @pi sem uma linha ACTIVE no registro que aponte para ela. A criação da conta é idempotente por alias, então a sua próxima tentativa adota a mesma conta e não cria uma segunda. Uma falha nunca deixa uma linha no registro ou um ISPB retido.
A resposta ainda traz um objeto provisioning com um campo failedStep. Ele é legado e sempre null para todo cadastro deste serviço, porque um cadastro malsucedido não deixa nenhuma linha.
Os estados do ciclo de vida são ACTIVE, SUSPENDED e CLOSED.
Como o dinheiro chega a um participante indireto
Acompanhe um Pix recebido. O indireto não chama nenhum endpoint nesse caminho. O seu plugin faz o trabalho.
-
“Esta conta pode receber?” Antes de o Banco Central mover o dinheiro, ele pergunta se a conta de destino aceita o crédito. A pergunta chega até você, porque o participante indireto não tem linha com o SPI. Com o recurso ligado, o plugin responde pelo cliente de um indireto a partir do registro do indireto, e não a partir da conta. Ele lê o registro quando o seu catálogo não atende o ISPB do recebedor. Em uma devolução, ele o lê apenas quando o seu catálogo não atende nenhuma das partes, e consulta o ISPB do pagador quando o ISPB do recebedor não nomeia nenhum participante indireto.
ACTIVEé válido.CLOSEDrespondeAC07, e qualquer outro estado respondeAC06.- O plugin não verifica se a conta existe no core do indireto. Um crédito para uma conta que o indireto não tem cai na posição
@pidele. - Na validação síncrona, quando o plugin não consegue ler o registro ou a flag do recurso, ele responde
500.
- Um Pix chega para uma conta na instituição indireta. Ele alcança a sua participação direta pelo webhook de cash-in do plugin.
-
A resolução encontra o indireto pelo ISPB no bloco do recebedor, no registro de participação. Há quatro desfechos, e apenas o primeiro credita um indireto:
- O ISPB corresponde a uma participação
ACTIVE. O crédito é lançado no alias@pi_{ispb}dessa participação. - O ISPB é o seu próprio. É o seu livro, e o CRM resolve a conta como de costume.
- O ISPB não corresponde a nada, ou corresponde a uma participação suspensa ou encerrada. O plugin recusa o crédito com
404 PIX-0119e não lança nada. A JD chama esta rota apenas depois que o SPI liquidou o crédito, então o dinheiro já está na sua conta de liquidação no Banco Central. Concilie esseendToEndIdcom o seu extrato do SPI. - A resolução falha por infraestrutura. O plugin nem credita nem recusa. Ele responde um erro, para que a JD entregue o crédito de novo. Ele falha fechado de propósito e não adivinha nada sobre dinheiro.
- O ISPB corresponde a uma participação
-
O crédito cai em
@pi_{ispb}. O plugin registra a movimentação contra essa posição de liquidação e vincula seu próprio registro de transação ao indireto. Ele grava o aviso na mesma transação de banco de dados que o crédito. Ou os dois existem, ou nenhum existe. -
O plugin envia um aviso assinado ao
delivery.endpointUrlque você cadastrou, com o payload da JD byte a byte. O plugin chama a instituição, não o contrário. - O participante indireto credita o próprio cliente, no próprio core.
A entrega é pelo menos uma vez. O plugin pode enviar o mesmo aviso de novo após uma falha que permite nova tentativa, ou após uma queda entre o envio e o lançamento contábil. O
endToEndId continua o mesmo entre as tentativas. Um receptor deve deduplicar pelo endToEndId e tratar um payload repetido como já processado.
O
secret armazenado é apenas-escrita. Toda leitura de um indireto retorna delivery.secret como ***, então um secret perdido é substituído com um PATCH, nunca recuperado.Quando o aviso nunca chega
O envio pode falhar de forma permanente. As tentativas se esgotam, ou o endpoint responde
4xx. Depois disso, o plugin marca o aviso como inválido e nunca mais o tenta. O dinheiro fica correto de qualquer forma. A movimentação é real, e a instituição não sabe dela.
GET /v1/indirects/{indirectId}/transactions fecha essa lacuna. Ele retorna as movimentações que se liquidaram na conta @pi_{ispb} de um indireto. Por isso ele é o registro definitivo, não o aviso. Uma instituição que o lê em um agendamento não depende mais da entrega. Ele é também a única forma de ler de novo uma janela depois de uma indisponibilidade própria.
Listar as transações de um participante indireto traz as regras que tornam uma conciliação correta. Elas cobrem a janela since semiaberta obrigatória, centavos inteiros, paginação por cursor do mais antigo para o mais novo e o limit limitado silenciosamente. Ler o feed não muda nada e não reenvia nenhum aviso.
Devolvendo um Pix que um participante indireto recebeu
A devolução de um Pix que caiu na posição de um participante indireto sai desse indireto. Envie
POST /v1/refunds com o indirectId desse participante. A devolução então debita a posição @pi_{ispb} dele. Ela não precisa de accountId: quem devolve é o recebedor que o Pix original nomeia. Com um parceiro por trás da requisição, um accountId responde 400 PIX-0061.
O indirectId deve nomear exatamente o participante que recebeu o Pix original. O plugin não move dinheiro, e responde:
422 PIX-0135quando o Pix original caiu na posição de um indireto, e a devolução não temindirectId.404 PIX-0147quando a devolução nomeia outro participante indireto, ou quando o Pix original caiu em uma conta direta e a devolução tem umindirectId.
indirectId então passa pelas mesmas checagens de uma ordem de pagamento: 422 PIX-0111 quando a funcionalidade está desligada, 404 PIX-0095 para um participante desconhecido e 422 PIX-0100 quando a participação não está ACTIVE.
Hospedando o QR code sob o certificado próprio da instituição
Por padrão, o plugin publica o QR code dinâmico de um indireto sob a sua participação direta. Um indireto pode publicar o próprio em vez disso. Defina
qrCertificate.ownCertificate com um publicBaseUrl em PATCH /v1/indirects/{indirectId}. A instituição então serve o documento assinado a partir do próprio host.
A instituição não pode produzir as chaves de validação sozinha. A JDPI mantém o certificado e faz a assinatura. GET /v1/indirects/{indirectId}/jwks retorna o JWK Set desse indireto. A instituição o publica no próprio host, e PSPs pagadores validam contra ele. Busque-o quando configurar o certificado, e de novo após cada rotação.
O status do ciclo de vida deliberadamente não controla essa leitura. Um indireto suspenso ou encerrado ainda tem QR codes ativos em circulação. Sem a chave, a validação deles quebraria.
Lendo o registro
limit limitado silenciosamente.
Um GET por id, e a lista, sempre mostram a verdade atual. Os caminhos do dinheiro leem uma projeção em cache. Cada réplica pode levar até o TTL do cache de resolução para ver uma suspensão. O TTL é de 30 segundos por padrão. O bloqueio do ledger vindo de suspend se aplica no momento da chamada.
Suspendendo, reativando e encerrando
PATCH /v1/indirects/{indirectId} carrega no máximo uma action de ciclo de vida: suspend, reactivate ou close. Cada uma escreve tanto no ledger quanto no registro.
suspendpara tanto o envio quanto o recebimento na conta@pido indireto. O próprio ledger recusa o dinheiro desse participante. Por isso uma suspensão vale mesmo em uma réplica cujo cache de resolução está desatualizado.reactivateretoma os dois.closeé permanente.CLOSEDé terminal, e depois disso o plugin recusa também toda mudança de campo na linha.
@pi de forma permanente, mas a mantém. Um cadastro posterior do mesmo ISPB reaproveita essa conta. A conta fica desbloqueada, ativa de novo, e mantém o histórico contábil.
Um PATCH só com campos, sem action, muda esses campos. Um PATCH vazio não muda nada e retorna a linha atual. Isso não é um erro.
O que dá errado
As páginas de operação listam cada recusa desse registro, com o status e a condição exata. Elas também dizem o que o plugin gravou ou não gravou quando recusou. Veja Cadastrar um participante indireto, Atualizar um participante indireto, Consultar um participante indireto, Listar participantes indiretos, Consultar o JWK Set de um participante indireto e Listar as transações de um participante indireto. A Lista de erros do Pix JD traz cada código, com o
detail exato de cada um.
O status diz se você deve repetir, não a família do código.
PIX-0107 e PIX-0123 são a mesma condição, vista de duas formas. Um 409 significa que o trilho leu a configuração e constatou que a chave não existe. Apenas um operador pode mudar isso, então repetir entra em loop para sempre. Um 503 significa que a leitura não terminou. O trilho não sabe que a chave está ausente, então repetir é correto. Nenhum dos dois grava nada.Para onde ir agora
- Hospedar participantes indiretos: o provisionamento, com a chave de criptografia, a postura e a chamada de cadastro.
- Cadastrar um participante indireto: o contrato completo da requisição e cada recusa.
- Listar as transações de um participante indireto: o feed de conciliação, com as regras completas de paginação.
- Atualizar um participante indireto: mudanças de campo, ações de ciclo de vida e a configuração de certificado próprio.
- Pix Direto via JD: a participação direta sobre a qual tudo isso roda, e como as movimentações liquidadas chegam ao Midaz.

