Skip to main content
This integration is optional. Matcher is a stand-alone product that works independently. Use this guide only if you want to integrate Matcher with Midaz Ledger.
Matcher can integrate natively with Midaz Ledger, enabling seamless reconciliation of ledger transactions against external sources like bank statements and payment gateways.

Overview


The Midaz integration provides:
  • Direct ledger access: Query transactions directly from Midaz without file exports
  • Real-time sync: Automatic transaction synchronization as entries are posted
  • Unified authentication: Single auth system via lib-auth
  • Tenant isolation: Schema-per-tenant architecture ensures data separation

Prerequisites


Before configuring the Midaz integration:
  • Midaz Ledger instance running and accessible
  • API credentials with transaction read permissions
  • Network connectivity between Matcher and Midaz
  • Matching tenant configuration in both systems

Configuration


Environment variables

Configure Matcher to connect to your Midaz instance:
# Midaz connection
MIDAZ_BASE_URL=https://api.midaz.example.com
MIDAZ_GRPC_ADDRESS=midaz-grpc.example.com:50051

# Authentication (shared with midaz)
AUTH_SERVICE_URL=https://auth.example.com
AUTH_CLIENT_ID=matcher-service
AUTH_CLIENT_SECRET=your-secret

# Connection settings
MIDAZ_CONNECTION_TIMEOUT=30s
MIDAZ_REQUEST_TIMEOUT=60s
MIDAZ_MAX_RETRIES=3

Create Midaz source via API

cURL
curl -X POST "https://api.matcher.example.com/v1/contexts/ctx_abc123/sources" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "name": "Midaz Main Ledger",
   "type": "MIDAZ",
   "description": "Primary ledger for operating accounts",
   "direction": "INTERNAL",
   "settings": {
     "ledger_id": "ldg_001",
     "account_filter": {
       "type": "ASSET",
       "path_prefix": "assets:cash"
     },
     "sync_mode": "realtime",
     "lookback_days": 30
   }
 }'

Response

{
  "id": "src_midaz_001",
  "context_id": "ctx_abc123",
  "name": "Midaz Main Ledger",
  "type": "MIDAZ",
  "direction": "INTERNAL",
  "status": "CONNECTED",
  "settings": {
    "ledger_id": "ldg_001",
    "account_filter": {
      "type": "ASSET",
      "path_prefix": "assets:cash"
    },
    "sync_mode": "realtime",
    "lookback_days": 30
  },
  "connection_status": {
    "connected": true,
    "last_sync": "2024-01-20T10:00:00Z",
    "transaction_count": 15420
  },
  "created_at": "2024-01-15T10:00:00Z"
}

Source settings

SettingTypeDescription
ledger_idStringTarget ledger ID in Midaz
account_filter.typeStringAccount type filter (ASSET, LIABILITY, etc.)
account_filter.path_prefixStringAccount path prefix to include
account_filter.accountsArraySpecific account IDs to include
sync_modeStringrealtime or batch
lookback_daysIntegerDays of historical data to include
exclude_pendingBooleanExclude pending entries

Authentication


Matcher uses the same authentication system as Midaz via the shared lib-auth library.

Service-to-service auth

Matcher authenticates to Midaz using OAuth 2.0 client credentials:
# Token exchange happens automatically
# Matcher requests: POST /oauth/token
{
 "grant_type": "client_credentials",
 "client_id": "matcher-service",
 "client_secret": "your-secret",
 "scope": "ledger:read transactions:read"
}

Required scopes

ScopePermission
ledger:readRead ledger metadata
transactions:readRead transaction entries
accounts:readRead account information

Tenant context

All requests include the tenant context header:
X-Tenant-ID: tenant_001
This ensures transactions are queried from the correct tenant schema.

Querying transactions


Automatic sync

With sync_mode: realtime, Matcher subscribes to Midaz events and receives transactions as they’re posted. When a transaction is created in Midaz, it publishes an event to the message queue. Matcher consumes the event, stores the transaction locally, and queues it for matching—all without manual intervention.
Event-driven ingestion

Manual query

For batch operations or historical data:
cURL
curl -X POST "https://api.matcher.example.com/v1/sources/src_midaz_001/sync" \
 -H "Authorization: Bearer $TOKEN" \
 -H "Content-Type: application/json" \
 -d '{
   "date_from": "2024-01-01",
   "date_to": "2024-01-31",
   "force_refresh": false
 }'

Response

{
  "sync_id": "sync_001",
  "source_id": "src_midaz_001",
  "status": "IN_PROGRESS",
  "date_range": {
    "from": "2024-01-01",
    "to": "2024-01-31"
  },
  "progress": {
    "total_expected": 5000,
    "fetched": 0,
    "new": 0,
    "updated": 0
  },
  "started_at": "2024-01-20T10:30:00Z"
}

Query filters

Filter transactions when syncing:
{
  "date_from": "2024-01-01",
  "date_to": "2024-01-31",
  "filters": {
    "operation_types": [
      "CREDIT",
      "DEBIT"
    ],
    "min_amount": 100.0,
    "currencies": [
      "USD",
      "EUR"
    ],
    "metadata": {
      "source_system": "treasury"
    }
  }
}

Transaction mapping


Midaz transactions are automatically mapped to Matcher’s schema:
Midaz FieldMatcher FieldNotes
idtransaction_idUnique entry ID
amountamountDecimal amount
asset_codecurrencyISO currency code
created_atdateEntry date
descriptionreferenceEntry description
metadata.external_refexternal_idExternal reference if present
operationtypeCREDIT or DEBIT

Custom field mapping

Override default mapping for specific use cases:
{
  "settings": {
    "field_mapping": {
      "reference": "metadata.invoice_number",
      "counterparty": "metadata.vendor_name",
      "external_id": "metadata.bank_reference"
    }
  }
}

Tenant isolation


Matcher inherits Midaz’s schema-per-tenant isolation model.

How it works

  1. Each tenant has a dedicated PostgreSQL schema
  2. All queries are scoped to the tenant’s schema
  3. Connection pooling maintains schema context
  4. No cross-tenant data access is possible
Database: matcher_db
├── tenant_001 (schema)
│ ├── transactions
│ ├── matches
│ └── exceptions
├── tenant_002 (schema)
│ ├── transactions
│ ├── matches
│ └── exceptions
└── shared (schema)
 ├── field_maps
 └── rules_templates

Tenant resolution

Tenant is determined from the authentication token:
{
  "sub": "user_123",
  "tenant_id": "tenant_001",
  "roles": [
    "reconciliation_admin"
  ]
}

Error handling


Connection errors

{
  "error": "MIDAZ_CONNECTION_ERROR",
  "message": "Failed to connect to Midaz at midaz-grpc.example.com:50051",
  "details": {
    "retry_count": 3,
    "last_error": "connection refused"
  },
  "suggestion": "Check network connectivity and Midaz service status"
}

Authentication errors

{
  "error": "MIDAZ_AUTH_ERROR",
  "message": "Failed to authenticate with Midaz",
  "details": {
    "status_code": 401,
    "reason": "invalid_client"
  },
  "suggestion": "Verify client credentials and required scopes"
}

Sync errors

{
  "error": "MIDAZ_SYNC_ERROR",
  "message": "Partial sync failure",
  "details": {
    "total_expected": 5000,
    "fetched": 4500,
    "failed_batches": [
      {
        "batch": 10,
        "error": "timeout"
      }
    ]
  },
  "suggestion": "Retry failed batches or increase timeout"
}

Monitoring


Connection health

curl -X GET "https://api.matcher.example.com/v1/sources/src_midaz_001/health" \
 -H "Authorization: Bearer $TOKEN"

Response

{
  "source_id": "src_midaz_001",
  "status": "HEALTHY",
  "connection": {
    "connected": true,
    "latency_ms": 45,
    "last_successful_query": "2024-01-20T10:29:00Z"
  },
  "sync": {
    "mode": "realtime",
    "last_event_received": "2024-01-20T10:29:55Z",
    "events_processed_24h": 1250,
    "lag_seconds": 5
  }
}

Sync statistics

curl -X GET "https://api.matcher.example.com/v1/sources/src_midaz_001/stats" \
 -H "Authorization: Bearer $TOKEN"

Best practices


Enable sync_mode: realtime for contexts with daily reconciliation. This ensures transactions are available for matching immediately after posting.
Use account_filter.path_prefix to limit transactions to relevant accounts. This reduces data volume and improves matching performance.
Configure lookback_days based on your reconciliation cycle. Daily reconciliation typically needs 7-14 days; monthly needs 45-60 days.
Track the delay between Midaz posts and Matcher availability. High lag may indicate queue backup or processing issues.
Store matching-relevant data in Midaz metadata fields (invoice numbers, bank references) and map them to Matcher fields for better match rates.

Next steps