Skip to main content
A fresh deployment of this rail posts every Pix movement into one Midaz organization and one ledger — the pair its binding names. Since version 2.0.2 that is the default, not the ceiling: one deployment can serve an economic group — a holding that operates, say, a payment institution, a credit fintech, and a bank — with each regulated institution keeping its own organization and each organization keeping as many ledgers as it needs. The whole feature is a toggle. It lives in a single systemplane key, and while that key is empty the rail behaves byte for byte as it did before the key existed. Nothing on this page is required reading for a deployment that is one institution with one book — stop at Setting up the rail and you are done.

One roof, several cabinets, several drawers


Think of the deployment as a back office with a wall of filing cabinets. Each cabinet belongs to one regulated institution: its files never mix with another institution’s, because a regulator audits each institution on its own. Inside a cabinet, drawers separate lines of business — one for the wallet product, one for the credit product. On the wall hangs an index card saying which cabinets and drawers exist. A clerk files money movements only into a drawer the index names; a movement addressed to a drawer that is not on the index is handed back, never filed somewhere “close enough”. The rail’s vocabulary maps one to one: Two consequences fall straight out of the model:
  • Every posting carries its scope. Each transaction is written into the organization and ledger it resolved to, so each institution’s records are complete on their own — regulatory and accounting reports come out per institution without untangling a shared book.
  • The rail never guesses a scope. A movement whose organization and ledger cannot be proved against the catalog is refused before any posting. Booking money into the wrong institution’s ledger is the one mistake this design exists to make impossible.

The toggle: an empty key means off


The catalog key starts empty, and empty is the not provisioned sentinel: the tenant posts only into the organization and ledger its binding names, exactly as before the key existed. Writing a catalog turns the feature on; the pair the binding names remains the default — the scope every flow posts in unless it resolves another one.
The default organization and ledger are never in the catalog. Their accounting routes stay in the routing.* keys and their asset and clearing account in their own configuration, exactly as Setting up the rail provisions them. The catalog declares only what the binding does not: repeating the default ledger there would give one book two sources of truth, so the rail refuses a catalog that redeclares it.
Deployment mode decides how far the toggle reaches. A single-tenant deployment is one direct participant, so its catalog can add ledgers of its own organization — additional books, same institution. An entry carrying any other ISPB is refused at read time, because a single-tenant deployment holds one JD credential and cannot authenticate as a second institution. Acting as several participants — several ISPBs under one roof — is the multi-tenant shape, where the tenant is the group and each additional participant is provisioned with a JD credential of its own.

Activate it, step by step


1

Complete the ordinary setup first

The catalog extends a working deployment; it does not replace the setup. Run Setting up the rail end to end: the binding you write there makes your institution the default participant and its ledger the default book. If your deployment is one institution with one book, stop there — with the catalog key empty, nothing on this page changes anything.
2

Provision the additional book in Midaz and the CRM

Each additional ledger is provisioned with the same recipe the setup page walks, pointed at the new book instead of the default one:
  1. The organization and the ledger. An additional ledger of your own institution goes under your existing organization. An additional participant (multi-tenant) gets an organization of its own, because one Midaz organization is exactly one regulated institution.
  2. The asset and the accounts on the new ledger, including its own external clearing account — an alias such as @external/BRL is unique within a ledger, so each book needs its own.
  3. The operation routes on the new ledger, one credit-and-debit pair per money profile the book serves. Unlike the default ledger, these UUIDs do not go into the routing.* keys — they go inside the catalog document in the next step.
  4. The holder records and aliases in the CRM, addressed with the X-Organization-Id of the organization that owns the book. The alias’s ledgerId is what tells the rail which book an account banks in, so on an organization with more than one ledger every alias must name its ledger.
3

Write the catalog

The catalog is written once, to PUT /system/tenant_policy/organizations, with the same wrapping the binding uses: the body is {"value": ...} and value is a string whose content is a JSON document — a list with one entry per cataloged organization, each naming its participant’s ISPB, its Midaz organization, and that organization’s ledgers with their routes. Your own organization appears here when it carries additional ledgers — never re-declaring the default ledger — and an additional participant’s organization always does.This is the document, unwrapped, with placeholder identifiers:
On a single-tenant deployment the entry carries your own ISPB and your own organization id, and the ledgers are the additional books; the identifiers differ per participant only in multi-tenant.routes takes up to ten profiles — in, inQrCode, out, outReversal, intraPsp, intraPspReversal, medDebit, medCredit, pixautomaticoDebit, pixautomaticoReversal — each a pair of Midaz operation route UUIDs. Only in and inQrCode are mandatory: every catalog ledger exists to receive, so a book that can serve neither inbound leg is a provisioning mistake, not a narrower book. The other eight are optional; a flow that needs an absent one refuses with 409 PIX-0105, naming the profile, the organization, and the ledger — the same behavior an unset routing.* key produces on the default book.Let jq do the string escaping, as with the binding:
A successful write answers 204. Read the key back the way you read the binding back — the value returns as a quoted string carrying your document:
The key is hot-reloadable: write it with the app running and it takes effect on the running deployment, with no restart and no redeploy.
4

Know what the validators refuse, and when

The catalog is validated twice, and the split matters: the write validator catches everything that can be judged from the document alone, so a malformed catalog is refused at the admin surface instead of discovered on a payment.Refused at write time, with 400 validation_error:
  • a value that is not a string, or anything after the first JSON document;
  • an unknown field at any depth — a mistyped route-profile name included, so a typo cannot pass as a silently absent profile;
  • an ispb that is not exactly 8 digits; an organizationId, ledgerId, or route leg that is not a non-zero UUID;
  • an empty externalAlias or asset; a route pair missing either leg; an organization declaring no ledger;
  • a missing in or inQrCode profile on any ledger;
  • a duplicated ispb, organizationId, or ledgerId anywhere in the document. A 403 is a missing permission, never a rejected value.
Refused at read time, because only the running rail knows your binding and mode. These surface on the money path, before any posting:
5

Understand how money routes once it is on

  • Every transaction is recorded in its scope. The posting carries the organization and ledger it resolved to, so each institution’s book stays complete and auditable on its own.
  • An inbound credit finds its own book. The receiver ISPB picks the organization; the account’s CRM record picks the ledger. An organization with a single ledger tolerates a record that names none — there is nothing to choose between; one with several does not, and refuses with 500 PIX-0120 rather than guess.
  • An outbound order names the paying participant when the tenant is several. POST /v1/transactions takes an optional payerIspb. A tenant acting as one participant omits it and behaves exactly as before; a tenant acting as several must supply it — omitted is 422 PIX-0127, and an ISPB the tenant does not act as is 422 PIX-0128 — because picking a payer arbitrarily would debit another institution’s customer.
  • Between two ledgers of the same organization, the transfer is internal. An alias is unique within a ledger, so it cannot be one posting: the rail reserves the amount on the payer’s ledger against its clearing account, posts the final credit on the payee’s ledger against its clearing account, then commits the reserve. Both legs book on each ledger’s own intraPsp route pair, and each book stays internally double-entry.
  • Between two organizations of the same tenant, the payment is ordinary Pix. Two organizations are two regulated participants, so the money travels the settlement rail (SPI) exactly as a payment to any other institution would — same tenant is never “same book”.
  • Each additional participant authenticates as itself. Orders that leave an additional organization are sent to JD under that participant’s own credential, provisioned per ISPB. A participant whose credential is not provisioned refuses its first order with 409 PIX-0092 and releases the hold, and the readiness probe reports it down naming that ISPB before traffic reaches it.

Turning it off


Writing an empty string back to tenant_policy/organizations is accepted and returns the tenant to default-only behavior — the same state as before activation. It does not undo anything already booked: money posted in an additional scope stays in that ledger, and it is no longer reachable through this rail — any flow that resolves to a scope the catalog no longer declares refuses with 500 PIX-0120 before touching the ledger. So empty the key only when nothing routes to the additional books anymore: no accounts whose records point at them, no in-flight payments, and no hosted participant mounted on an additional organization. To retire one book while keeping the feature, remove that ledger’s entry and leave the rest of the catalog in place — the same read-time proof applies, scope by scope.

Where to go next


Setting up the rail

The provisioning chain this page extends: the ledger objects, the CRM records, the routing keys, and the binding that names the default pair.

The direct and indirect model

How this axis differs from hosting indirect participants: an indirect is a position inside your book; an additional organization is a direct participant with books of its own.

Environment variables

The deploy-time configuration, and which values live in systemplane keys instead.

Pix JD error list

Every PIX-NNNN code this page names, with its status and the detail text the response carries.