Skip to main content
A fresh Direct Pix via JD deployment starts, responds to its health probe, and refuses every payment. Nothing is broken. The rail needs a chain of objects before it can move money. Until they exist, it refuses the payment and does not guess. This page gives that chain as one trail, in the order you act, for a service that is already installed and running, with the failure that each missing link causes. Lerian runs this setup with you during onboarding. Use the page to know what must exist, what each value means, and how to prove that it landed.
On Lerian Cloud, Lerian runs the service. You give Lerian the signing values of step 2, only if JD requires them. Every other step on this page is yours to run, because it creates your own books and your own settings.
The order is a dependency chain, not a preference. Midaz refuses each link while the previous one does not exist. The limits step sends a payment, so it comes after the settings. Where a step can run in any order, the page says so.

The trail

  1. Check the prerequisites, and grant access to whoever runs the setup.
  2. Set up payment-order signing, only if JD requires it for your institution.
  3. Provision the ledger and the holder records in Midaz and in the CRM.
  4. Create the accounting routes in Midaz.
  5. Configure the rail: the plugin settings.
  6. Send the payment that creates the limit rows.
  7. Prove the setup is complete, up to a registered Pix key.
  8. Add the optional capabilities that your institution needs.

Step 1: Check the prerequisites

You talk to three services. Do not confuse them. That is the most common first mistake. You also need:
  • The Pix plugin running. The plugin’s settings API must be enabled (SYSTEMPLANE_ENABLED=true). Without it, every configuration call of step 5 answers 404. On SaaS, Lerian enables it.
  • On a self-hosted deployment, the permission of the key lookup route, entries-lookup (action get), on the Pix roles in the Access Manager. With JD Courier, also courier-ownership (action post), for the Courier ownership route. Without its permission, each route answers 403.
  • A bearer token for the ledger, one for the CRM, and one for the plugin, issued by the Access Manager. They can come from different audiences.
  • The right access for whoever runs the setup. The access step below lists the groups. Without it, the calls answer 403.
  • Your institution’s ISPB: the 8-digit identifier of your accreditation at BACEN.
The examples below use these shell variables. Where the rail already has a name for a value, the variable has that same name. Every UUID, document, and ISPB is a placeholder. Use the values from your own environment.
Midaz has an onboarding surface and a transaction surface, and this rail configures them separately. When one address serves both, give the two variables the same value. The calls below follow the object that each one creates.
The CRM requires the X-Organization-Id header on every collection route. Without it, the CRM does not scope a query to your organization. You then get another record, or nothing at all.

Grant access to whoever runs the setup

Why this step exists. Setting up Pix touches three products: the ledger (Midaz), the account holder records (CRM), and the Pix JD plugin itself. Each one checks its own permission. Without it, the call answers 403 and nothing is written. If you are an administrator, you already have everything. The Administrator group (admin-group) has full access to the three products. Go on to the next step. To let someone else run the setup without being an administrator, add that person to the groups below in the Access Manager, through the console or through Update a User. This is the minimum access that covers every step on this page: After the setup, reduce the access. For day-to-day operation (payments, limits, indirect participants), Plugin PIX JD Contributor (plugin-br-pix-jd-contributor-group) is enough. It cannot change the Pix configuration, so an operating mistake does not change how your money is routed. For someone who only reads, use Plugin PIX JD Viewer (plugin-br-pix-jd-viewer-group). Prove that the access is right before you go on. With that person’s token:
  • GET /system/-/catalog must answer 200.
  • The first write under /system, the integration binding in step 5, must answer 204 No Content. A 403 there means the person is not in Plugin PIX JD Editor.

Step 2: Set up payment-order signing, if JD requires it

JD can require a signature on each payment order for your institution. Confirm this with JD. If JD requires it, follow Payment-order signing now, before any payment. If JD does not require it, skip this step.
Do this step before step 6. Step 6 sends a payment. If JD requires signed orders and the order is not signed, JD refuses it. After repeated refusals, JD blocks all debits of your institution.

Step 3: Provision the ledger and the holder records

These steps run against Midaz and the CRM. They create the accounts that the rail posts to, and the holder records that it reads counterparties from. Access: Midaz Editor for the ledger steps, and Plugin CRM Contributor for the two CRM steps.
1

Create the organization and the ledger

The organization is your institution in the books. The ledger is the book it posts to. An institution can hold more than one ledger. This rail posts to the default one: the pair that you create here and name in the binding of step 5. Every flow uses that pair until you declare more books in the organization catalog. For the catalog, see Multi-organization and ledgers.
A 201 with no id in the body is not a success. Nothing can address the new object, not you and not a later cleanup. Stop there. Do not carry an empty id into the next step.
2

Create the BRL asset, and wait for it to appear

The asset is the currency of the records. For Pix it is BRL.
Both 201 and 409 are good answers. A ledger addresses an asset by its code, so “already exists” means the same as success.Two things happen here. The second one is easy to miss:
  1. The ledger starts to accept accounts in that asset. Before this, it refuses every account with 0034 Asset Code Not Found.
  2. Midaz creates the @external/BRL account with the asset. It is the one account in a book that you can debit before any credit. The opening balance comes from it. It is also the counterparty of every settlement with the outside world.
The 201 arrives before the asset shows in the listing. A script that creates the asset and then creates an account on the next line fails from time to time. Poll the listing until the code appears, with a deadline:
3

Create one account per role

Create one account for each role that your environment uses: payer, payee, and the other roles of your product.
You use the account id two times later: in the CRM link below, and as the accountId of the business calls you make to the plugin. The alias (@payer) is the short name that the book reads and posts by. It comes back in the next step.
4

Create the holder in the CRM

The holder owns the account. The plugin reads who a counterparty is from the CRM, not from the ledger.
Four fields need care:
  • document must be a valid CPF or CNPJ, with correct check digits.
  • type follows the document length. Use NATURAL_PERSON for a CPF (11 digits) and LEGAL_PERSON for a CNPJ (14). The plugin turns this type into the number that goes to JD. A wrong type files the party as the wrong kind of person.
  • externalId holds the account’s ledger alias (@payer). The CRM calls it an external correlation identifier, so it looks optional. For this rail it is not. A Pix key registration for a holder without externalId answers 422 PIX-0108.
  • addresses.primary.city is required for QR codes and Pix Automático. It is the receiver city in the code. Without it, the plugin refuses to generate a QR and answers 422 PIX-0033 for payee.city. No payment path reads the field, so its absence shows only when someone generates a QR.
5

Link the holder to the account

The holder and the ledger account both exist, but nothing joins them yet. This step is the join. The plugin uses it to find where to credit an incoming Pix and which account a payment debits.
Never send bankId as an empty string. An empty string is a value. It records “this account belongs to the institution whose ISPB is empty”, which is worse than no value. If you do not have the ISPB yet, omit the field.
Check what the CRM recorded, and always filter. An unfiltered query returns the first link in the organization. That is how a check can approve the account of somebody else:
6

Fund the accounts

A new account holds zero, and you cannot pay from an empty account. The opening credit comes from @external/BRL, which Midaz created with the asset, two steps above.
In production, you do not load a direct participant’s customer account this way. Money enters as a real inbound Pix, or as an inflow that the customer makes. The @external/BRL load has two uses. One is the @pi settlement position of a hosted indirect participant. The other is a test or homologation environment, where no real Pix arrives. See The direct and indirect model for what each position is.
Two identical bodies sent within 300 seconds make one posting. Midaz collapses the repeat: the second POST answers 201 with the first posting’s id, and nothing moves. Use that property when you retry.A retry of a top-up whose response you lost must send the exact same body, description included, within 300 seconds. The account then cannot get two credits. After that window, read the account balance before you send the top-up again. Change the description only for a new top-up. A new description on a real retry adds a second credit by mistake.
You can name @external/BRL in a body, but not in a path. The alias contains a slash, and the ledger route does not decode it. A balance read by path answers 404 or an empty 200. Posting from it is normal. Reading it that way is not.

Step 4: Create the accounting routes

Each money path on this rail has its own pair of Midaz operation routes: one credit leg and one debit leg. There are eleven profiles. Step 5 stores the UUIDs of the routes in the plugin. Access: Midaz Editor. Which pairs you need. You do not create eleven pairs. Create one pair for each of the six payment profiles. The other profiles reuse a pair, or stay empty: So the minimum is six pairs, twelve routes. A profile can reuse the pair of another profile only when the money moves in the same direction. A MED refund and a Pix Automático reversal bring money in, like in. A MED debit and a Pix Automático debit take money out, like out. Create each leg with one call:
Each call returns {"id": "..."}. Keep each id with its profile and its leg. Step 5 writes them.
You can invert the direction, and nothing warns you. A debit leaves the payer, so it is the source leg. A credit arrives, so it is the destination leg. If you swap them, every posting still answers success, in the wrong direction. No status code reports this.
Look up a route by title before you create it. With two routes of the same title, the next lookup can pick either one. If you provision the same book more than once, list first. If your ledger validates routes, also create one Midaz transaction route per profile that you use. A ledger that validates routes refuses a posting that names no transaction route. See Transaction routing entities for how to create one.

Step 5: Configure the rail

The plugin keeps its live configuration in its systemplane. You write these values over the admin API, and they take effect without a redeploy or a restart. Every write is a PUT to /system/<namespace>/<key> with a {"value": ...} body. A success answers 204 No Content, with no body. The tenant comes from the validated bearer, never from the URL or the body. Access: every write below needs Plugin PIX JD Editor. These values decide which institution the plugin presents to JD and which accounts it posts to, so only that group and the administrators can change them. Any Pix JD group can read them.
Ask the plugin what it expects. Do not trust a copy. GET /system/-/catalog lists every key with its type and description, and GET /system/-/catalog/<namespace>/<key> describes one key. The catalog is the source of truth. This page is a copy of it, and copies age.
1

Write the JD integration binding

What it is. The binding is one document with three fields. The ISPB is the 8-digit number that identifies your institution at the Banco Central. It goes on every Pix message, and the plugin cannot sign as your institution without it. The other two fields name your default Midaz organization and ledger, from step 3.Where it lives. None of the three is a deployment variable. They come from the systemplane key tenancy/jd_integration_binding, with no environment fallback. While the key is empty, the plugin starts and answers its health probe. It refuses every payment on every money route.What breaks without it. 409 PIX-0092, “Tenant Pix integration not provisioned”. The response text asks you to contact support and does not name the key. Search for the code.
A restart does not help. The plugin reads the key on every call, not at boot. A deployment with no ISPB still has no ISPB after a restart. Write the key while the app runs, and the next request passes, with no redeploy.
The value is a JSON document inside a string. The body is always {"value": ...}. Here value is not an object. It is a string, and its content is a JSON document. The plugin stores it with the inner quotes escaped:
Let jq do the escaping:
The binding names your default organization and ledger. Every flow posts in that pair unless it resolves another one through the organization catalog. If organizationId or ledgerId is absent or unusable, the plugin refuses every posting, refund, and inbound credit with 409 PIX-0106.
The decoder is strict. It refuses an unknown field, and that refusal stops the whole write. This includes the old routeProfiles field, because the accounting routes moved to the routing.* keys below. A document from an old configuration therefore fails. The decoder also refuses anything after the first JSON document.
How to check it. Read the key back. It holds no secret, so the value comes back in the clear:
If value comes back as an object and not as a quoted string, you wrote the wrong form. If it comes back "", the write did not happen. Check the status code of the PUT. The real proof is behavior: money routes stop their 409 PIX-0092 answers.You cannot store a malformed ISPB through this route. The write validator uses the same decoder as the read. It refuses a 7-digit ISPB or a broken UUID at once, with 400 validation_error. You do not find the error on the first payment.
So you do not see 409 PIX-0121, “Tenant Pix integration ISPB invalid”, when you follow this page. That code means that the binding exists and its ispb is not 8 digits, and this route cannot create that state. It shows only when a value reached the key by another path. Examples are a direct write to the plugin’s database, or a write from before the validator. Its message names both the key and the field to correct.
A 400 here is not a permission problem. A missing permission answers 403. This 400 means that the value is wrong. The response does not say which field, so check the 8 digits first. That is the most common mistake.
The plugin accepts the empty string, which means “not provisioned”. If you write it, the deployment goes back to 409 PIX-0092. Do not do it in an environment that pays.
2

Check the connection to JD

Why this step exists. The binding is the last piece that the plugin needs to talk to JD as your institution. Before any money moves, prove that the conversation works: ask JD for the list of Pix participants.Access: any Pix JD group, Plugin PIX JD Viewer included.
The plugin asks JD on every call, with no cache, so the answer is current. List Pix participant bank IDs describes the response.A 200 with the participant list, like this one (trimmed), proves that the plugin reached JD and received the participant catalog:
If you get a different response, redo the previous steps, starting with access and the integration binding. If the response is still different, contact our support team.
3

Write the routing keys

What it is. For each profile, the UUID pair of the Midaz operation routes from step 4. All the keys live in the tenant_policy namespace. Each one holds a string: the UUID of a route that exists in Midaz.
What breaks without it. The rail reads the six payment profiles together on every payment. A missing leg of any of the six refuses every payment flow with 409 PIX-0105: sent, received, QR code, and between your own accounts. A missing MED or Pix Automático leg refuses only that flow. refund_sent is the exception. While both of its legs are empty, the rail posts a devolução that you send on the out routes.
How to check. Each write answers 204. If you do not use MED or Pix Automático, leave their profiles empty. That flow then refuses, which is better than a posting on an undefined route.The transaction route of each profile. Each profile also has an optional key, tenant_policy/routing.<profile>.transaction_route. Fill it when your ledger validates routes. The plugin then names that transaction route on every posting of the profile. Without it, such a ledger refuses the posting, and the plugin answers PIX-4011. When your ledger does not validate routes, leave these keys empty.
The empty string means “not yet provisioned”, and the plugin accepts it. The plugin refuses the all-zeros UUID. That UUID has a valid format, but it is never a real Midaz identifier. Do not fill unused routes with zeros as a “safe empty”. Use the empty string.
4

Set the posting asset and the clearing account

What it is. The asset of every posting, and the external account that stands for the world outside your institution. Two systemplane keys in the tenant_policy namespace hold them, in both deployment modes:Both values are plain strings:
Write the code and the alias, not the Midaz UUIDs of the asset and the account. The key names end in _id, but the plugin puts the value in the posting as it is.
What breaks without it. Both keys start empty. Until you write both, the plugin refuses every posting, refund, and inbound credit with 409 PIX-0106, “Tenant ledger configuration missing”. The response names the binding fields and the two keys. This code is not PIX-0105. A tenant can have all route legs and still refuse every posting, because the asset or the clearing account is empty.The deployment variables MIDAZ_ASSET_ID and MIDAZ_EXTERNAL_ID no longer exist. The plugin ignores them.
5

Set the daily window

These are the two ends of the window for transaction limits. Both are integers from 0 to 23. They are clock hours, not timestamps. Both are systemplane keys in both deployment modes.
Why this step exists. Each institution picks the hours when its daily transaction-limit window opens and closes. The plugin cannot guess them.The values above are an example. Use your own. The JSON type stays the same: an integer, without quotes. A value outside the range answers 400. The plugin does not clamp it to the bound.
6

Declare whether this deployment hosts indirect participants

plugin-br-pix-jd.indirects/enabled declares what the tenant is. A plain direct participant sets it to false.
This is a configuration write, so it needs Plugin PIX JD Editor. The value is a JSON boolean, without quotes. {"value":"true"} answers 400. If this deployment settles Pix for other institutions, step 8 sets it to true.

Step 6: Create the transaction limit rows with one small payment

The product has no route to create what this step needs. What you expect. You provision an account and read its available limit. What happens. GET /v1/limits/available answers 404 PIX-0063, “The specified transaction limit was not found in the system. Please verify the identifier and try again.” GET /v1/limits answers {"data":[]}. The account looks broken, but it is not. This is the normal first state of an account that never sent a payment. PIX-0063 names the thing that the plugin looked up. The error list shows its generic form, “The specified entity was not found in the system”. This route puts transaction limit in it. The code and the 404 stay the same. Why you cannot create the rows. There is no creation route. PATCH /v1/limits updates rows that already exist. One thing creates the rows: the limit check of an outbound payment. When an account sends its first payment, the plugin finds no rows. It creates the defaults, reads them again, and continues. So send one small outbound payment. One cent is enough.
If JD requires signed payment orders for your institution, finish step 2 first. Otherwise JD refuses this payment. After repeated refusals, JD blocks all debits of your institution.
Access: the payment needs Plugin PIX JD Contributor or Plugin PIX JD Editor. Changing a limit later with PATCH /v1/limits needs the same.
An account with no limit rows does not have an unlimited limit. If the plugin cannot create the limits, it refuses the payment.Do not try to force the rows with an amount above the ceiling. The balance check runs before the limit check. A large amount fails for insufficient funds before the limit check, and no rows appear. Send an ordinary payment that fits the balance.
How to check. GET /v1/limits changes from {"data":[]} to a list. /v1/limits/available changes from 404 to 200. Do this step after every step above. The payment must post, so it needs every value of steps 3 to 5. Without a route, for example, it fails with PIX-0105.

Step 7: Prove the setup is complete

Run these checks in order. Each one fails for a different reason. That makes the sequence more useful than a single smoke test.
  1. The ledger accepts an account. Create a disposable account and delete it. If the book refuses, the refusal names the missing link. Without this check, the same problem comes back later as “account not found” inside a payment flow, three layers from its cause. Do not fund the probe account: Midaz refuses to delete an account with a balance. The delete needs Midaz Editor.
  2. The binding reads back as a quoted string with your ISPB, your organization, and your ledger, as shown in step 5.
  3. GET /v1/banks answers 200 with the list of Pix participants, as in the connection check of step 5.
  4. The two tenant_policy/midaz.* keys read back with the asset code and the clearing account alias.
  5. The alias query returns your account, filtered by document, branch, and account number.
  6. A money route stops its 409 PIX-0092 and 409 PIX-0106 answers. Use the call that you already make. You need no test harness.
  7. GET /v1/limits returns a list for the account that sent the payment of step 6.
  8. A Pix key registers and shows in the account’s list. This check goes through the CRM, the binding, and the DICT at JD.
For the last check, register a random key for the account. Send keyType 4 and no key: the DICT creates the key value. A random key needs no confirmation code, so it goes to the DICT on the first call:
Then list the account’s keys:
The key is in the list. A CPF or CNPJ key also goes to the DICT on the first call. An email key or a phone key also needs the 6-digit code that the plugin sends through the notification provider of Environment variables: without a configured provider, it stays at status 2, “awaiting confirmation”. Register a Pix key entry describes every answer. Remove a test key when you are done with DELETE /v1/entries/{entryId}.

Step 8: Add the optional capabilities

Your setup ends at step 7 if your institution is one direct participant with one book, and it settles only the Pix of its own customers. Otherwise, continue on the page that applies:
  • Several books, or several direct participants on one deployment. Follow Multi-organization and ledgers.
  • Settling Pix for other institutions. Follow Hosting indirect participants. It adds the encryption key, the hosting posture, the registration of each indirect participant, its own QR certificate if it has one, and the funding of its settlement position.

What each skipped step looks like

Every refusal below is a 409, except the limits one. None of them goes away if you wait. Each one goes away when you provision the missing value.
When PIX-0092 stops, payments do not always pass. It is the first barrier, not the last. With the binding in place, an empty accounting route still answers PIX-0105. A missing asset or clearing account still answers PIX-0106. Three codes have three causes and three different fixes.

Do not retry a 409. Do retry its 503 sibling

The status tells you if the problem is your setup or an outage. A 409 above is positive knowledge of absence. The rail read your configuration and found nothing there. Only an operator can supply the value, so a repeat of the request gets the same answer. A client that retries on this code loops forever. Each of those responses names what to set. Most of those conditions have a 503 sibling. It applies when the rail could not read the configuration at all. The rail knows nothing about what you provisioned. The response names the dependency that failed, and a retry is the right action.
The accounting routes are the exception. PIX-0105 has no 503 sibling. A route leg that is absent, empty, malformed, the all-zeros UUID, or unreadable gives the same 409. So a PIX-0105 alone does not tell “this leg has no value” from “the configuration plane did not answer”. Read the legs back before you decide that it is an outage.
Both codes of each pair refuse the operation. Neither one posts or stores anything. The difference is only your next action, so they are separate codes.PIX-0121 is a special case. You cannot cause it through the admin API, because the write validator refuses a malformed ispb before it is stored. It shows only when a value reached the key by another path. See the binding in step 5.
Two more codes name a service that the rail depends on. They are not about your setup. Retry them: The Pix JD error list has every code, with the exact detail of each one.

Where to go next

Environment variables

The deploy-time configuration of this rail: JD connectivity, the ledger and CRM endpoints, QR hosting, and the notification providers.

Multi-organization and ledgers

Extend this setup to several books, or several direct participants, on the same deployment: the organization catalog, as a feature toggle.

Indirect participants

What a hosted participation is, how an inbound credit reaches one, its lifecycle, and every refusal it can answer.

Direct Pix via JD

How settled Pix movements land in Midaz, and how the two systems correlate.

Pix JD error list

Every PIX-NNNN code, its status, and the detail text of the response.