> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lerian.studio/llms.txt
> Use this file to discover all available pages before exploring further.

# Participantes indiretos

> Como uma instituição menor chega ao Pix por meio da sua participação direta: a posição de liquidação que cadastrar um indireto cria, como o dinheiro dela chega, como ela fica sabendo de uma movimentação, como ela devolve um Pix e o que cadastrar um indireto custa de forma permanente.

A sua instituição é um **participante direto**. Ela tem presença própria no sistema de liquidação nacional do Pix (SPI), e este plugin é o caminho dela até lá. Um **participante indireto** é uma instituição menor sem conexão própria. Ele chega ao SPI pela sua. Ele é cliente da sua instituição, não cliente do trilho.

Essa relação existe no registro do seu tenant, não no BACEN. Você a cria com `POST /v1/indirects`. [O modelo direto e indireto](https://docs.lerian.studio/pt/interfaces/pix-jd/direct-and-indirect-model.md) 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.

> [!NOTE]
>
> Esta página explica o que uma participação hospedada é e como ela se comporta. [Hospedar participantes indiretos](https://docs.lerian.studio/pt/interfaces/pix-jd/hosting-indirect-participants.md) 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.

| | Responsabilidade |
| - | - |
| Banco Central / SPI | movimenta dinheiro **entre instituições**. Não conhece clientes finais |
| JD | a ponte técnica: entrega os avisos e assina os QR codes. Não toma decisões de negócio |
| Você, o participante direto | a **posição de liquidação** de cada participante indireto: a conta `@pi` no seu ledger, que espelha o limite que você administra na JD. Você garante que ela existe, continua ativa e tem saldo. Você também responde pela liquidação entre instituições |
| O participante indireto | **as contas dos seus próprios clientes** |

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.

> [!NOTE]
>
> 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.

Duas operações recusam de imediato enquanto a flag está desligada, com `422 PIX-0111`. Uma é a escrita de certificado de QR próprio em `PATCH /v1/indirects/{indirectId}`. A outra é `GET /v1/indirects/{indirectId}/jwks`.

> [!NOTE]
>
> 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.

**O cadastro é atômico: um `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`.

> [!WARNING]
>
> `PENDING_PROVISIONING` é um estado **aposentado** com uma aresta afiada. Nenhum cadastro entra nele agora. `GET /v1/indirects` ainda o aceita como filtro, para que linhas de antes do cadastro atômico continuem sendo lidas como elas mesmas. Mas esse estado **não tem transições de ciclo de vida em nenhuma direção**. Você não pode suspender nem encerrar uma linha legada. Toda ação sobre ela responde `409 PIX-0094`.
>
> Uma linha assim é legível, mas **não roteável**, e ainda mantém o ISPB dela contra o índice de unicidade aberto. Por isso ela bloqueia um novo cadastro dessa instituição, e nenhuma chamada de API consegue liberá-la. Limpar uma é uma decisão de operador e de dados, e não é uma operação de API. Se o seu tenant é mais antigo que o cadastro atômico, rode `GET /v1/indirects?status=PENDING_PROVISIONING` uma vez para encontrar essas linhas.

## 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.

1. **"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. `CLOSED` responde `AC07`, e qualquer outro estado responde `AC06`.
   * 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 `@pi` dele.
   * Na validação síncrona, quando o plugin não consegue ler o registro ou a flag do recurso, ele responde `500`.
2. **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.
3. **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-0119` e 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 esse `endToEndId` com 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 plugin nunca armazena em cache uma falha de resolução. Por isso um indireto que acabou de ficar ativo nunca fica escondido atrás de uma resposta negativa anterior.
4. **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.
5. **O plugin envia um aviso assinado** ao `delivery.endpointUrl` que você cadastrou, com o payload da JD byte a byte. O plugin chama a instituição, não o contrário.
6. **O participante indireto credita o próprio cliente**, no próprio core.

> [!WARNING]
>
> O plugin não consulta o CRM no caminho indireto. O destino vem do registro de participação, nunca dos dados do chamador. Isso é proposital. Se um participante indireto tivesse o seu próprio ISPB por engano, créditos para o seu próprio livro cairiam na conta dele. Por isso a verificação "esse ISPB é nosso?" roda antes da verificação "corresponde a um participante indireto?".

O aviso carrega dois headers que a instituição receptora verifica:

| Header | Valor |
| - | - |
| `X-Lerian-Signature` | `sha256=` seguido do HMAC-SHA256 em hex dos bytes exatos do corpo bruto, assinado com o `secret` cadastrado desse indireto. |
| `X-Lerian-Timestamp` | O horário de envio em segundos Unix-epoch, para que o receptor limite o replay com sua própria janela de validade. |

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.

> [!NOTE]
>
> 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](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/list-indirect-participant-transactions.md) 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-0135` quando o Pix original caiu na posição de um indireto, e a devolução não tem `indirectId`.
* `404 PIX-0147` quando a devolução nomeia outro participante indireto, ou quando o Pix original caiu em uma conta direta e a devolução tem um `indirectId`.

O `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

***

```bash theme={null}
# one participation
curl -s "$PIX_JD_BASE_URL/v1/indirects/$INDIRECT_PARTICIPANT_ID" -H "Authorization: Bearer $PIX_JD_BEARER_TOKEN" | jq

# the list — filters are exact and combine with AND
curl -s -G "$PIX_JD_BASE_URL/v1/indirects" -H "Authorization: Bearer $PIX_JD_BEARER_TOKEN" \
  --data-urlencode 'status=ACTIVE' --data-urlencode 'limit=25' | jq
```

[Listar participantes indiretos](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/list-indirect-participants.md) traz as regras de paginação por cursor e o `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.

```bash theme={null}
curl -s -X PATCH "$PIX_JD_BASE_URL/v1/indirects/$INDIRECT_PARTICIPANT_ID" \
  -H "Authorization: Bearer $PIX_JD_BEARER_TOKEN" -H 'Content-Type: application/json' \
  -d '{"action":"suspend"}'
```

* **`suspend`** para tanto o envio quanto o recebimento na conta `@pi` do 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. **`reactivate`** retoma os dois.
* **`close`** é permanente. `CLOSED` é terminal, e depois disso o plugin recusa também toda mudança de campo na linha.

Um encerramento não apaga nada. O plugin bloqueia a conta `@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.

> [!WARNING]
>
> **Não existe DELETE em `/v1/indirects`, e `CLOSED` é terminal.** A única saída do registro é `close`, que mantém a linha e a conta. Um cadastro feito por engano em um tenant ativo permanece no registro desse tenant para sempre. O ISPB dele fica retido contra a restrição de unicidade aberta até você encerrá-lo. Todo código de instituição que você usa é permanente. Verifique `name`, `ispb` e o endpoint de entrega antes de chamar.

## 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](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/register-an-indirect-participant.md), [Atualizar um participante indireto](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/update-an-indirect-participant.md), [Consultar um participante indireto](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/get-an-indirect-participant.md), [Listar participantes indiretos](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/list-indirect-participants.md), [Consultar o JWK Set de um participante indireto](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/get-an-indirect-participant-jwk-set.md) e [Listar as transações de um participante indireto](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/list-indirect-participant-transactions.md). A [Lista de erros do Pix JD](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/pix-jd-error-list.md) traz cada código, com o `detail` exato de cada um.

> [!NOTE]
>
> **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](https://docs.lerian.studio/pt/interfaces/pix-jd/hosting-indirect-participants.md)**: o provisionamento, com a chave de criptografia, a postura e a chamada de cadastro.
* **[Cadastrar um participante indireto](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/register-an-indirect-participant.md)**: o contrato completo da requisição e cada recusa.
* **[Listar as transações de um participante indireto](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/list-indirect-participant-transactions.md)**: o feed de conciliação, com as regras completas de paginação.
* **[Atualizar um participante indireto](https://docs.lerian.studio/pt/reference/interfaces/pix-jd/update-an-indirect-participant.md)**: mudanças de campo, ações de ciclo de vida e a configuração de certificado próprio.
* **[Pix Direto via JD](https://docs.lerian.studio/pt/interfaces/pix-jd/direct-pix-via-jd.md)**: a participação direta sobre a qual tudo isso roda, e como as movimentações liquidadas chegam ao Midaz.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.