Skip to main content
This guide walks through reconciling Pix transactions between Midaz Ledger and BACEN (Brazil’s Central Bank) settlement data using Matcher. It covers both Pix sent (cash-out) and Pix received (cash-in), from configuration to daily operation and exception handling. By the end of this guide, you will have a fully configured Matcher context that automatically reconciles your Pix transactions against BACEN’s SPI settlement extracts on a daily basis.

Pix transaction flows


Understanding how Pix transactions flow through the system is essential for configuring reconciliation correctly. The two flows below show what Matcher needs to reconcile on each side.

Pix sent (cash-out)

Pix sent cash-out flow
  1. Client initiates Pix — The end user triggers a Pix payment via the app or API.
  2. Plugin creates initiation — The Pix plugin creates an initiation record and resolves the destination account via DICT lookup.
  3. Plugin processes payment — The plugin debits the client account in Midaz (transaction in pending status) and sends the payment instruction to BTG/SPI.
  4. Settlement confirmed — BTG sends a webhook confirming settlement. The Midaz transaction is committed.
  5. Matcher reconciles — Matcher compares the committed Midaz transaction against the corresponding entry in the BACEN SPI settlement extract.

Pix received (cash-in)

Pix received cash-in flow
  1. Inbound Pix arrives — SPI/BTG sends a synchronous webhook containing the inbound Pix data.
  2. Plugin validates — The Pix plugin validates the payload and approves the transaction.
  3. Credit transaction created — The plugin creates a CREDIT transaction in Midaz for the recipient account.
  4. Settlement confirmed — The settlement webhook confirms the transaction is final.
  5. Matcher reconciles — Matcher compares the Midaz credit transaction against the corresponding entry in the BACEN SPI settlement extract.
In both flows, the endToEndId is the unique identifier that links the Midaz transaction to the BACEN settlement record. This is the primary key for reconciliation.

Configuration step-by-step


1

Create the context

Create a reconciliation context for Pix transactions. Use type 1:1 because each Pix transaction has exactly one corresponding BACEN settlement entry.
{
  "name": "Pix Daily Reconciliation",
  "type": "1:1",
  "interval": "daily",
  "feeToleranceAbs": 0,
  "feeTolerancePct": 0,
  "autoMatchOnUpload": false
}
Pix transactions have no intermediate fees or partial settlements. A Pix of R150.00inMidazmustappearasexactlyR 150.00 in Midaz must appear as exactly R 150.00 in the BACEN extract. Set both tolerance values to zero.Setting autoMatchOnUpload to false gives you control over when matching runs, which is important when you need both sources ingested before executing.
See the full request schema at Create context.
2

Create the sources

Each context needs two sources: one for Midaz transactions and one for the BACEN settlement extract.Source A — Midaz (type LEDGER):
{
  "name": "Midaz - Pix Transactions",
  "type": "LEDGER",
  "config": {
    "ledger_id": "your-ledger-id",
    "currency": "BRL"
  }
}
When Midaz is integrated with Matcher (see Midaz integration), transactions are ingested automatically via event-driven sync. No manual upload is needed for this source.Source B — BACEN SPI extract (type CUSTOM):
{
  "name": "BACEN SPI Settlement Extract",
  "type": "CUSTOM",
  "config": {
    "description": "Daily SPI settlement file from BACEN"
  }
}
The BACEN source uses type CUSTOM because the SPI settlement file is uploaded manually or via an automated pipeline each day.
See the full request schema at Create source.
3

Create field maps

Field maps tell Matcher how to translate fields from each source into the canonical fields used for matching.The following table shows the mapping between Matcher’s canonical fields and the fields in each source:
Matcher fieldMidaz (automatic)BACEN extract (field map)
transaction_ididid_liquidacao
amountamountvalor
currencyasset_codemoeda
datecreated_atdata_liquidacao
referencemetadata.endToEndIdend_to_end_id
When Midaz is integrated with Matcher, standard fields are mapped automatically. The endToEndId lives in transaction metadata and requires a custom field mapping override in the source settings (see Midaz integration — custom field mapping):Midaz source — field map with endToEndId override:
{
  "mapping": {
    "id": "transaction_id",
    "amount": "amount",
    "asset_code": "currency",
    "created_at": "date",
    "metadata.endToEndId": "reference"
  }
}
BACEN source — field map:
{
  "mapping": {
    "id_liquidacao": "transaction_id",
    "valor": "amount",
    "moeda": "currency",
    "data_liquidacao": "date",
    "end_to_end_id": "reference"
  }
}
See Create field map for the full request schema and Midaz integration for details on automatic field mapping.
4

Create match rules

Match rules define how Matcher compares transactions across sources. For Pix reconciliation, two rules cover the vast majority of scenarios.Rule 1 — Exact match by endToEndId (priority 1):
{
  "type": "EXACT",
  "priority": 1,
  "config": {
    "matchAmount": true,
    "matchCurrency": true,
    "matchDate": true,
    "matchReference": true,
    "datePrecision": "DAY",
    "caseInsensitive": false,
    "referenceMustSet": true,
    "matchScore": 100
  }
}
This rule resolves approximately 95% of cases. The endToEndId is unique per Pix transaction across the entire ecosystem. When the reference, amount, currency, and date all match, it is a confirmed reconciliation with maximum confidence. Note that caseInsensitive is set to false because endToEndId values are case-sensitive, and referenceMustSet is true to ensure both sides carry the endToEndId before comparing — this prevents false positives on amount and date alone.Rule 2 — Date tolerance fallback (priority 51):
{
  "type": "DATE_LAG",
  "priority": 51,
  "config": {
    "maxDays": 1,
    "minDays": 0,
    "inclusive": true,
    "direction": "ABS",
    "feeTolerance": 0,
    "matchScore": 85,
    "matchCurrency": true
  }
}
A Pix initiated at 23:58 may settle in BACEN on the following calendar day. This rule allows a 1-day window to cover D+1 settlement scenarios. The lower matchScore of 85 signals that these matches should be reviewed with slightly more attention, though they are still valid. Note that this rule relies on amount and currency matching only — the endToEndId comparison is handled by Rule 1.
See Create match rule for the full request schema and all available rule types.
5

Activate and schedule

Once all configuration is in place, activate the context and create a daily schedule.Activate the context:
curl -X PATCH "https://api.matcher.example.com/v1/config/contexts/{contextId}" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{ "status": "ACTIVE" }'
Create a schedule to run daily at 07:00 UTC:
curl -X POST "https://api.matcher.example.com/v1/contexts/{contextId}/schedules" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "cronExpression": "0 7 * * *",
   "enabled": true
 }'
Running at 07:00 UTC provides enough margin for D+1 settlements to appear in the BACEN extract and for the daily file to be uploaded before the matching run executes.
See Update context and Create schedule for the full request schemas.

Daily operation


Once configured, the daily reconciliation workflow follows five steps.
1

Upload BACEN extract

Upload the previous day’s SPI settlement file to the BACEN source. Matcher accepts both CSV and JSON formats.
curl -X POST "https://api.matcher.example.com/v1/imports/contexts/{contextId}/sources/{sourceId}/upload" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: multipart/form-data" \
 -F "file=@bacen_spi_2026-03-17.csv" \
 -F "format=csv"
This step can be automated via a pipeline that fetches the SPI file and uploads it before the scheduled matching run.
2

Midaz data available automatically

With the Midaz integration configured, transactions are continuously ingested into Matcher as they are committed. No manual upload is needed for Source A. See Midaz integration for setup details.
3

Matcher runs at 07:00 (or manually)

The scheduled run executes automatically at 07:00 UTC. To run matching manually, use the run endpoint.
curl -X POST "https://api.matcher.example.com/v1/matching/contexts/{contextId}/run" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{ "mode": "DRY_RUN" }'
Use DRY_RUN first to preview results without committing them. When satisfied, run again with COMMIT:
curl -X POST "https://api.matcher.example.com/v1/matching/contexts/{contextId}/run" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{ "mode": "COMMIT" }'
4

Review results

After the run completes, retrieve the matched groups to see the results.
curl -X GET "https://api.matcher.example.com/v1/matching/runs/{runId}/groups" \
 -H "Authorization: Bearer $TOKEN"
Each group shows the matched Midaz transaction and its corresponding BACEN settlement entry, along with the rule that matched them and the confidence score.
5

Resolve exceptions

Unmatched transactions appear as exceptions. These require investigation — a transaction present in one source but not the other, or a mismatch in amount or date beyond the configured tolerance.Review exceptions, determine the root cause, and resolve them by force matching, ignoring, or correcting the underlying data.
Always run a DRY_RUN first when testing new rules or after configuration changes. This prevents unintended matches from being committed.

Practical example — one day of data


The following example illustrates a complete reconciliation run for March 17, 2026.

Midaz transactions (Source A)

IDendToEndIdAmountTypeDate
txn-001E123456789202603170001150.00Pix OUT2026-03-17 10:15
txn-002E9876543212026031700423200.50Pix IN2026-03-17 11:30
txn-003E55566677720260317009989.90Pix OUT2026-03-17 23:58
txn-004E111222333202603170007500.00Pix IN2026-03-17 14:00

BACEN SPI extract (Source B)

id_liquidacaoend_to_end_idvalordata_liquidacao
liq-8801E123456789202603170001150.002026-03-17
liq-8802E9876543212026031700423200.502026-03-17
liq-8803E55566677720260317009989.902026-03-18
liq-8804E444555666202603170055750.002026-03-17

Match results

endToEndIdMidazBACENResultRule
E12345…0001txn-001liq-8801MatchEXACT (score: 100)
E98765…0042txn-002liq-8802MatchEXACT (score: 100)
E55566…0099txn-003liq-8803Match (D+1)DATE_LAG (score: 85)
E11122…0007txn-004Exception
E44455…0055liq-8804Exception

Analysis

  • txn-001 and txn-002: Exact match on endToEndId, amount, currency, and date. Rule 1 resolved these with confidence score 100.
  • txn-003: Pix initiated at 23:58, settled in BACEN on 2026-03-18. Rule 2 (DATE_LAG with 1-day window) resolved this with confidence score 85.
  • txn-004: Present in Midaz but absent from BACEN. Possible settlement failure or SPI timeout. Investigate the transaction status via the Pix plugin.
  • liq-8804: Present in BACEN but absent from Midaz. An inbound Pix that was not processed. Check webhook delivery or reprocess the message.

Handling Pix exceptions


The following table covers the most common Pix exception scenarios and recommended actions.
ScenarioProbable causeRecommended action
In Midaz, not in BACENSettlement failure, SPI timeout, rejected transactionCheck transaction status in the Pix plugin. If rejected, revert in Midaz.
In BACEN, not in MidazInbound Pix not processed, webhook failureReprocess the message. Create a manual transaction if needed.
Amount mismatchRare in Pix (no intermediate fees). Possible rounding error.Investigate original records. Force match if the difference is acceptable.
Date mismatch (>1 day)Held transaction, reprocessingVerify it is the same Pix. Force match or ignore.

Force match

When you have confirmed that two records represent the same Pix transaction but Matcher could not match them automatically, use force match.
curl -X POST "https://api.matcher.example.com/v1/exceptions/{exceptionId}/force-match" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "notes": "Confirmed same Pix via endToEndId lookup in SPI",
   "overrideReason": "D+2 settlement delay confirmed with BACEN"
 }'

Ignore transaction

When a transaction should be excluded from reconciliation (for example, a duplicate entry or an already-reversed Pix), mark it as ignored.
curl -X POST "https://api.matcher.example.com/v1/imports/contexts/{contextId}/transactions/{transactionId}/ignore" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "reason": "Pix reversal already processed — duplicate entry"
 }'
See Force match and Ignore transaction for the full request schemas.

Pix refunds (devoluções)


Pix refunds generate reverse transactions that also need reconciliation. When a refund is processed, the Pix plugin creates a new transaction in Midaz with:
  • The originalEndToEndId linking back to the original Pix transaction
  • A new returnIdentification (rtrId) that uniquely identifies the refund in SPI
BACEN’s settlement extract includes refund entries with both identifiers, allowing Matcher to reconcile them against the corresponding Midaz refund transactions. For low refund volumes, these can be reconciled within the same Pix Daily Reconciliation context. For high volumes, create a separate context dedicated to refund reconciliation. This simplifies exception triage and keeps refund metrics isolated from standard Pix flow metrics.
The Pix plugin supports refund initiation via POST /v1/transfers/{id}/refunds. Each refund carries the originalEndToEndId and a new returnIdentification for end-to-end tracking.

Best practices


The endToEndId is the unique Pix identifier across the entire ecosystem — from the initiating institution through SPI to the receiving institution. Ensure it is stored in Midaz transaction metadata and present in the BACEN extract. Without it, reconciliation falls back to amount and date matching, which is far less reliable.
Pix transactions near end of day may settle in BACEN on D+1. Scheduling Matcher for 07:00 UTC ensures all settlements from the previous day are included in the BACEN extract before matching runs. This eliminates false exceptions caused by timing.
When processing high Pix volumes, create two separate contexts — one for cash-out and one for cash-in. This simplifies exception triage, provides more granular metrics per flow, and allows independent scheduling if needed.
A healthy Pix reconciliation achieves greater than 99% automatic match rate. If the rate drops below 95%, investigate systemic issues such as plugin failures, BACEN format changes, or missing metadata in Midaz transactions.
Pix has no intermediate fees, partial settlements, or processing charges. If amounts diverge between Midaz and BACEN, it indicates a real problem — not rounding. Keep both feeToleranceAbs and feeTolerancePct at zero.
Always run a DRY_RUN before COMMIT, especially after rule or field map changes. This lets you review match results and catch configuration errors before they affect production data.

Key metrics


Track these metrics to monitor the health of your Pix reconciliation process.
MetricHealthy valueAlert threshold
Automatic match rateAbove 99%Below 95%
Daily exceptionsBelow 0.5% of volumeAbove 2%
Average resolution timeUnder 4 hoursOver 24 hours
Unresolved exceptions 48h+0More than 5
Use Matcher’s dashboard endpoints to monitor these metrics in real time. See Dashboard metrics.

Next steps


Contexts and sources

Learn how to configure and manage reconciliation contexts and data sources.

Match rules

Explore all available rule types and advanced matching configurations.

Midaz integration

Deep dive into automatic field mapping and real-time sync with Midaz Ledger.

Resolving exceptions

Detailed guide on investigating, force matching, and managing reconciliation exceptions.