This page covers provisioning. Indirect participants explains what a hosted participation is, how an inbound credit reaches one, how its lifecycle behaves, and every refusal it can answer.
To give an indirect participant its own Pix JD credential that reaches only its own Pix, see Partners in Pix JD.
Step 1: the delivery-secret encryption key
On SaaS, there is nothing to do in this step. Lerian configures the key for your tenant. Go to step 2. The rest of this step applies to a self-hosted deployment.
POST. That is the whole recovery.
This
409 means “provision it”, not “try again later”. The rail looked for the key and found that it does not exist. Only an operator can add it. A repeat of the call without that change fails the same way. That is why the code is a 409 and says nothing about a retry.Its sibling is 503 PIX-0123, “Indirect delivery key source unavailable”. You get it when the rail could not read the key, because its key source refused or did not answer. The rail then does not know that the key does not exist, because the read did not complete. The response names the dependency that failed, and a retry is the right action. Both codes refuse the registration and store nothing. The pair lets you tell an incomplete setup from an outage.
The format: exactly 64 hex characters. It is an AES-256 key (32 bytes), hex-encoded. That is 64 hex characters, not 63 and not 65.
An absent, blank, or malformed value all produce the same
409 PIX-0107. There is no default and no degradation to storing the secret in the clear. That is deliberate: a silent default here would store customer secrets encrypted with a key everybody knows.409 PIX-0107 to 201.
Step 2: declare that this tenant hosts indirect participants
Setplugin-br-pix-jd.indirects/enabled to true. This is a configuration write, so it needs Plugin PIX JD Editor.
204 when it accepts the value. The value is a JSON boolean, without quotes: {"value":"true"} answers 400.
Registration works with the posture off, so you can register participants before you enable it. The posture gates the money paths and the own QR certificate of step 4. With the posture off, step 4 answers 422 PIX-0111. Before you register anyone shows what each half does.
The read fails closed. When a money path “goes back to direct behavior” and nobody changed the key, this is the cause. Look at the systemplane.
Step 3: register an indirect participant
One call does the whole assembly. It checks the ISPB, creates the@pi_{ispb} settlement account in Midaz, and marks the participation active. It needs Plugin PIX JD Contributor or Plugin PIX JD Editor.
A
201 means that the participation is ready to use. The status is always ACTIVE. You do not need to poll.
Indirect participants has the refusals, the lifecycle actions, and how to read the registry back.
Step 4: configure its own QR certificate, if it has one
Skip this step for an indirect participant that has no QR certificate of its own at JD. Its dynamic QR codes are then signed with your certificate and served on your host, as for your own customers. Why this step exists. Some indirect participants have their own QR certificate at JD. JD then signs their dynamic QR codes with that certificate. The payer’s app checks the signature against the key set that it finds on the same host as the QR payload. So the payload and the key set of that participant must be on the participant’s own host, not on yours. Access: Plugin PIX JD Contributor or Plugin PIX JD Editor.1
Name the participant's host
Send
PATCH /v1/indirects/{indirectId} with the indirectId from the registration:The 77-character budget. JD caps the URL of a QR payload at 77 characters. The plugin builds it as
publicBaseUrl, then QRCODE_PAYLOAD_PATH, then, on SaaS, your institution’s 8-digit ISPB, then the type of the charge and an identifier of up to 22 characters. With the default QRCODE_PAYLOAD_PATH, qr, 37 characters are left for the host. A deployment with a longer QRCODE_PAYLOAD_PATH leaves fewer. Then each dynamic QR code for this participant answers 422 PIX-0112. Choose the host against that budget before you register it.A 200 returns the participation. Check that qrCertificate.ownCertificate is true and qrCertificate.publicBaseUrl is the host that you sent.2
Publish the participant's key set on its host
The participant cannot make its key set itself, because JD holds the certificate and signs. Read the key set from the plugin:The answer is an
application/jwk-set+json document. Serve those bytes as they are, with that media type, at https:// plus the participant’s host plus / plus QRCODE_JWK_PATH. With the default QRCODE_JWK_PATH, that is https://pix.indirect.example.com/v1/qrcodes/jwks. On SaaS, the path ends with one more segment: your institution’s 8-digit ISPB, for example https://pix.indirect.example.com/v1/qrcodes/jwks/12345678. Read the key set again each time JD rotates the participant’s certificate.The X-JWK-Set-Freshness header says fresh or stale. stale means that JD did not answer and the plugin served its last copy. Read it again before you publish.indirectId, the plugin does not serve its payload. The response carries payloadJws, and the participant serves it at payloadUrl on its own host. Create a dynamic QR code describes that response, and how to recover a payload that you did not receive.
Step 5: fund the position
The registration creates the@pi_{ispb} account empty. Inbound Pix credits it, but an outbound payment needs balance first. Without funds, every payment that leaves through that indirect participant fails for insufficient balance. You make the initial load. It mirrors the arrangement: at JD you administer that institution’s limit, and in your ledger you load its position.
The mechanics are the same as the funding step of Setting up the rail. Post a Midaz transaction from @external/BRL, with the same variables as that page. Only the destination changes, to the piAccountAlias from the registration. It is a Midaz transaction, so it needs Midaz Editor or Midaz Contributor.
close with 409 PIX-0096 while the @pi account holds funds, available or on hold, and names the cause. Bring the position to zero before you close. A funded position keeps the participation open until you do.
How to check. Read the account’s balance back in Midaz. The surest check is behavior: an outbound payment through the indirect participant stops its refusals for insufficient balance.
Return a Pix that an indirect participant received
A Pix for a customer of an indirect participant lands on that participant’s@pi_{ispb} position. To return it, send POST /v1/refunds with the indirectId of that participant. The devolução then debits that position.
The plugin refuses the devolução with 422 PIX-0135 in three cases, and moves no money:
- the original Pix landed on an indirect participant’s position, and the refund has no
indirectId. - the refund names another indirect participant.
- the original Pix landed on a direct account, and the refund has an
indirectId.
indirectId passes the same checks as on a payment order. The hosting posture must be on, and the participation must be ACTIVE.
The systemplane keys of the indirects namespace
There are five keys, all in plugin-br-pix-jd.indirects, and all of them always exist. The three delivery and resolution keys take effect only when enabled is true, because they tune the money paths. validate_ispb_on_jd is the exception. It gates a step of registration, which works while enabled is still false.
204 when it accepts a value and 400 when the validator refuses it. Booleans and integers go without quotes. A value outside the range answers 400. The plugin does not clamp it.
The two steps that can stop the indirect flow on their own are not in this namespace. Identity is the systemplane key
tenancy/jd_integration_binding. Without it, every payment fails with 409 PIX-0092. The encryption key comes from a deployment variable or a secret store. Without it, every registration fails with 409 PIX-0107.
