POST /v1/indirects. El modelo directo e indirecto explica dónde se aloja el dinero de un participante indirecto: la cuenta de liquidación en el Banco Central, el límite en JD y la posición en tu ledger.
Esta página explica qué es una participación hospedada y cómo se comporta. Hospedar participantes indirectos cubre el aprovisionamiento: la clave de cifrado del secreto de entrega, la postura de hospedaje y la llamada de registro. Su numeración continúa desde la página de configuración. De esos pasos, solo la clave de cifrado rechaza todo registro por sí sola, así que aprovisiónala primero. La postura de hospedaje controla las rutas del dinero, no el registro, así que puedes registrar participantes antes de activarla.
Quién es responsable de qué
La mayoría de los errores operativos aquí vienen de buscar la respuesta en la columna equivocada.
En la práctica, un Pix para un cliente de un participante indirecto se detiene en ti. Llega a una cuenta que representa la posición de esa institución. Tú no sabes, y no puedes saber, cuál de sus clientes es el dueño del dinero. El participante indirecto acredita a su cliente en su propio core, después de recibir tu aviso.
Qué crea registrar uno
Un registro escribe una fila, y el plugin también crea la posición de liquidación de esa institución en Midaz. Es una cuenta del ledger cuyo alias empieza con
@pi_ y termina con el ISPB de la institución, el código de 8 letras mayúsculas o dígitos que BACEN asigna a cada participante. Un indirecto con ISPB 12345678 liquida en @pi_12345678.
Esa posición mantiene el dinero de esa institución. La respuesta la devuelve como piAccountAlias. El plugin deriva el alias de ispb. Tú nunca lo eliges, y un cliente nunca crea la cuenta.
Cada institución tiene una posición. Un crédito para un indirecto llega solo a la cuenta de ese indirecto. Por eso el dinero de una institución nunca se mezcla con el de otra, ni con el tuyo. El registro existe para ofrecer esa segregación.
El alias usa un guion bajo, no una barra:
@pi_12345678. Midaz rechaza una barra en un alias de usuario, así que un nombre con forma de ruta no resuelve a nada.Antes de registrar a nadie
La participación indirecta está apagada de forma predeterminada. El flag de systemplane por tenant
plugin-br-pix-jd.indirects/enabled debe estar activo, y su valor predeterminado es false.
El flag controla dos mitades, y se comportan distinto:
- La API de gestión funciona con el flag apagado. Puedes registrar participantes, listarlos y leer uno antes de la habilitación. La incorporación previa al lanzamiento es deliberada.
- Ningún dinero se mueve con el flag apagado. La resolución decide a qué indirecto pertenece un crédito entrante. Con el flag apagado, reporta la función como deshabilitada. Ningún crédito entrante va a una cuenta
@pi, y ningún indirecto puede originar una orden saliente.
422 PIX-0111. Una es la escritura del certificado QR propio en PATCH /v1/indirects/{indirectId}. La otra es GET /v1/indirects/{indirectId}/jwks.
La lectura falla de forma cerrada. Cuando una ruta de dinero “vuelve a comportarse como directa” y nadie cambió la clave, esta es la causa. Revisa el systemplane.
Registrar uno
1
Activa la función para el tenant
Define
plugin-br-pix-jd.indirects/enabled en true. El registro funciona sin ella, pero nada liquida hasta que esté activa.2
POST /v1/indirects
Envía el
name de la institución, su ispb, y un bloque delivery con endpointUrl y secret. La respuesta es 201. Trae indirectId, el identificador por el que se enruta cada llamada posterior, y piAccountAlias.3
Nada por lo que hacer polling: un 201 significa listo
Cada verificación se ejecuta antes de que el plugin escriba la fila del registro. Primero viene la verificación de unicidad del ISPB, luego la verificación opcional en el directorio de JD, y luego la creación de la cuenta
@pi_{ispb} en Midaz. Solo entonces el plugin escribe la fila, con estado ACTIVE. No hay estado intermedio ni nada que esperar.4
Ante una falla, corrige la causa y vuelve a hacer POST
Un paso fallido responde con un error codificado que nombra el paso. El plugin no escribe ninguna fila en el registro, así que el ISPB sigue libre. Corrige la causa y vuelve a enviar el POST. Esa es la recuperación. No hay una ruta de reintento separada, porque no queda ninguna fila a medio escribir para reanudar.
201 significa listo, y una falla no deja fila en el registro. En un 201, status siempre es ACTIVE. No escribas un cliente que haga polling hasta que el participante se vuelva enrutable. Ese bucle espera un cambio que no puede ocurrir.
Una falla puede dejar atrás solo la cuenta de liquidación, cuando ese paso tuvo éxito y uno posterior falló. El plugin no la revierte, y no causa ningún daño. Nada se enruta a una cuenta @pi sin una fila ACTIVE en el registro que apunte a ella. La creación de la cuenta es idempotente por alias, así que tu siguiente intento adopta la misma cuenta y no crea una segunda. Una falla nunca deja una fila en el registro ni un ISPB retenido.
La respuesta todavía trae un objeto provisioning con un campo failedStep. Es heredado y siempre null para todo registro de este servicio, porque un registro fallido no deja fila.
Los estados del ciclo de vida son ACTIVE, SUSPENDED y CLOSED.
Cómo el dinero llega a un participante indirecto
Sigue un Pix entrante. El indirecto no llama a ningún endpoint en esta ruta. Tu plugin hace el trabajo.
-
“¿Esta cuenta puede recibir?” Antes de que el Banco Central mueva el dinero, pregunta si la cuenta de destino acepta el crédito. La pregunta llega a ti, porque el participante indirecto no tiene línea al SPI. Con la función activada, el plugin responde por el cliente de un indirecto a partir del registro del indirecto, no a partir de la cuenta. Lee el registro cuando tu catálogo no atiende el ISPB del receptor. En una devolución, lo lee solo cuando tu catálogo no atiende a ninguna de las partes, y consulta el ISPB del pagador cuando el ISPB del receptor no nombra a ningún participante indirecto.
ACTIVEes válido.CLOSEDrespondeAC07, y cualquier otro estado respondeAC06.- El plugin no verifica que la cuenta exista en el core del indirecto. Un crédito para una cuenta que el indirecto no tiene llega a su posición
@pi. - En la validación síncrona, cuando el plugin no puede leer el registro o la flag de la función, responde
500.
- Llega un Pix para una cuenta en la institución indirecta. Alcanza tu participación directa a través del webhook de cash-in del plugin.
-
La resolución encuentra al indirecto a partir del ISPB en el bloque del receptor, en el registro de participación. Hay cuatro resultados, y solo el primero acredita a un indirecto:
- El ISPB coincide con una participación
ACTIVE. El crédito se contabiliza en el alias@pi_{ispb}de esa participación. - El ISPB es el tuyo. Es tu libro, y el CRM resuelve la cuenta como de costumbre.
- El ISPB no coincide con nada, o coincide con una participación suspendida o cerrada. El plugin rechaza el crédito con
404 PIX-0119y no registra nada. JD llama a esta ruta solo después de que el SPI liquidó el crédito, así que el dinero ya está en tu cuenta de liquidación en el Banco Central. Concilia eseendToEndIdcon tu extracto del SPI. - La resolución falla por infraestructura. El plugin no acredita ni rechaza. Responde un error, para que JD vuelva a entregar el crédito. Falla de forma cerrada a propósito y no supone nada sobre el dinero.
- El ISPB coincide con una participación
-
El crédito llega a
@pi_{ispb}. El plugin registra el movimiento contra esa posición de liquidación y vincula su propio registro de transacción con el indirecto. Registra el aviso en la misma transacción de base de datos que el crédito. O existen ambos, o no existe ninguno. -
El plugin envía un aviso firmado al
delivery.endpointUrlque registraste, con el payload de JD byte por byte. El plugin llama a la institución, no al revés. - El participante indirecto acredita a su propio cliente, en su propio core.
La entrega es al menos una vez. El plugin puede volver a enviar el mismo aviso después de una falla reintentable, o después de una caída entre el envío y el registro contable. El
endToEndId se mantiene igual entre intentos. Un receptor debe deduplicar por endToEndId y tratar un payload repetido como ya procesado.
El
secret almacenado es de solo escritura. Cada lectura de un indirecto devuelve delivery.secret como ***, así que un secreto perdido se reemplaza con un PATCH, nunca se recupera.Cuando el aviso nunca llega
El envío puede fallar de forma definitiva. Los reintentos se agotan, o el endpoint responde
4xx. Después de eso, el plugin marca el aviso como inválido y nunca lo vuelve a intentar. El dinero está correcto de cualquier forma. El movimiento es real, y la institución no lo sabe.
GET /v1/indirects/{indirectId}/transactions cierra esa brecha. Devuelve los movimientos que se liquidaron en la cuenta @pi_{ispb} de un indirecto. Por eso es el registro autoritativo, no el aviso. Una institución que lo lee según una programación ya no depende de la entrega. También es la única forma de volver a leer una ventana después de una interrupción propia.
Listar las transacciones de un participante indirecto da las reglas que hacen correcta una conciliación. Cubren la ventana since semiabierta obligatoria, los centavos como enteros, la paginación por cursor de más antiguo a más reciente y el recorte silencioso de limit. Una lectura del feed no cambia nada y no vuelve a enviar ningún aviso.
Devolver un Pix que recibió un participante indirecto
La devolución de un Pix que llegó a la posición de un participante indirecto sale de ese indirecto. Envía
POST /v1/refunds con el indirectId de ese participante. La devolución entonces debita su posición @pi_{ispb}. No necesita accountId: quien devuelve es el receptor que nombra el Pix original. Con un socio detrás de la solicitud, un accountId responde 400 PIX-0061.
El indirectId debe nombrar exactamente al participante que recibió el Pix original. El plugin no mueve dinero, y responde:
422 PIX-0135cuando el Pix original llegó a la posición de un indirecto, y la devolución no tieneindirectId.404 PIX-0147cuando la devolución nombra a otro participante indirecto, o cuando el Pix original llegó a una cuenta directa y la devolución tiene unindirectId.
indirectId pasa las mismas verificaciones que en una orden de pago: 422 PIX-0111 cuando la función está apagada, 404 PIX-0095 para un participante desconocido, y 422 PIX-0100 cuando la participación no está en ACTIVE.
Hospedar el código QR bajo el certificado propio de la institución
De forma predeterminada, el plugin publica el código QR dinámico de un indirecto bajo tu participación directa. Un indirecto puede publicar el suyo propio en su lugar. Define
qrCertificate.ownCertificate con un publicBaseUrl en PATCH /v1/indirects/{indirectId}. La institución entonces sirve el documento firmado desde su propio host.
La institución no puede producir las claves de validación por sí misma. JDPI mantiene el certificado y hace la firma. GET /v1/indirects/{indirectId}/jwks devuelve el JWK Set de ese indirecto. La institución lo publica en su propio host, y los PSP pagadores validan contra él. Obtenlo cuando configures el certificado, y de nuevo después de cada rotación.
El estado del ciclo de vida deliberadamente no controla esa lectura. Un indirecto suspendido o cerrado todavía tiene códigos QR activos en circulación. Sin la clave, su validación se rompería.
Leer el registro
limit.
Un GET por id, y la lista, siempre muestran la verdad actual. Las rutas del dinero leen una proyección en caché. Cada réplica puede tardar hasta el TTL de la caché de resolución en ver una suspensión. El TTL es de 30 segundos de forma predeterminada. El bloqueo del ledger de suspend se aplica en el momento de la llamada.
Suspender, reactivar y cerrar
PATCH /v1/indirects/{indirectId} lleva como máximo un action de ciclo de vida: suspend, reactivate o close. Cada uno escribe tanto el ledger como el registro.
suspenddetiene tanto el envío como la recepción en la cuenta@pidel indirecto. El ledger mismo rechaza el dinero de ese participante. Por eso una suspensión se mantiene incluso en una réplica cuya caché de resolución está desactualizada.reactivatevuelve a habilitar ambos.closees permanente.CLOSEDes terminal, y después el plugin rechaza también todo cambio de campo en la fila.
@pi de forma permanente pero la conserva. Un registro posterior del mismo ISPB reutiliza esa cuenta. La cuenta queda desbloqueada, activa de nuevo, y conserva su historial contable.
Un PATCH con solo campos y ningún action cambia esos campos. Un PATCH vacío no cambia nada y devuelve la fila actual. No es un error.
Qué sale mal
Las páginas de operación listan cada rechazo de este registro, con su estado y su condición exacta. También dicen qué escribió o no escribió el plugin cuando rechazó. Consulta Registrar un participante indirecto, Actualizar un participante indirecto, Obtener un participante indirecto, Listar participantes indirectos, Obtener el JWK Set de un participante indirecto y Listar las transacciones de un participante indirecto. La lista de errores de Pix JD tiene cada código, con el
detail exacto de cada uno.
El estado te dice si conviene reintentar, no la familia del código.
PIX-0107 y PIX-0123 son la misma condición, vista de dos formas. Un 409 significa que el riel leyó la configuración y encontró que la clave no existe. Solo un operador puede cambiar eso, así que un reintento se repite para siempre. Un 503 significa que la lectura no se completó. El riel no sabe que la clave está ausente, así que un reintento es correcto. Ninguno de los dos almacena nada.A dónde ir después
- Hospedar participantes indirectos: el aprovisionamiento, con la clave de cifrado, la postura y la llamada de registro.
- Registrar un participante indirecto: el contrato de solicitud completo y todo rechazo.
- Listar las transacciones de un participante indirecto: el feed de conciliación, con las reglas de paginación completas.
- Actualizar un participante indirecto: cambios de campo, acciones de ciclo de vida y la configuración del certificado propio.
- Pix Directo vía JD: la participación directa sobre la que se apoya todo esto, y cómo los movimientos liquidados llegan a Midaz.

