The Inner-Workings of Our Settlement
9-state settlement engine lifecycle - from intent submission through finality proof, covering fiat-to-fiat, fiat-to-crypto, and crypto-to-crypto settlement lanes.
received
→
validated
→
compliance_check
→
accepted
→
ready
→
submitted
→
confirmed
→
final
|
rejected
failed
cancelled
expired
01Settlement Types
The settlement engine supports three distinct lanes, each with different compliance paths and attestation requirements.
Fiat-to-Fiat (FI-to-FI)
Traditional cross-border bank settlement - complements existing SWIFT/DTCC rails with atomic finality.
Type: SIMPLE
Compliance: Single check (KYC/AML/sanctions)
Attestation: Auto-transition from accepted to ready
Travel Rule: FATF Wire Transfer Rule evaluation required
Party Data: Legal name, account ID, country, address
Example: Bank A (USD) → Bank B (EUR)
Fees: 3-5 bps (vs 15-40 bps SWIFT)
Fiat-to-Crypto (FI-to-Crypto)
On-ramp settlement for institutional crypto acquisition.
Type: SIMPLE or DVP
Compliance: Per-leg for DVP, single for SIMPLE
Attestation: DVP requires leg attestation per counterparty
Travel Rule: Applies to fiat leg
DVP Example: Fiat delivery (leg 1) vs crypto delivery (leg 2)
Example: Fund deposits USD → receives JIL/BTC/ETH
Crypto-to-Crypto
Cross-chain or same-chain token swaps with atomic execution.
Type: DVP or PVP
Compliance: Per-leg compliance, per-leg attestation
Attestation: Both legs must attest ready via POST /:id/attest-ready
PVP: Concurrent asset exchange (both legs atomic)
Example: Swap jBTC → jETH atomically
Finality: Both legs marked "settled" at FINAL
02Settlement State Machine - Happy Path
The 8-step happy path from client submission through finality. Each state transition is driven by the settlement engine's poll loop running at ENGINE_POLL_INTERVAL_MS (default 2000ms).
Client submits a settlement request via
POST /v1/settlements.
The engine creates a settlement record with a UUID and assigns a UETR (Unique End-to-End Transaction Reference).
An expiry timer is set (default 60 minutes via
SETTLEMENT_EXPIRY_MINUTES).
Travel rule evaluation runs immediately - if the settlement involves fiat legs, FATF Wire Transfer Rule
checks are applied to validate party data (legal name, account identifier, country, address).
If travel rule fails →
REJECTED.
If passes →
VALIDATED.
Client SDK
settlement-api :8050
travel rule passed
Schema validation complete and travel rule passed. The settlement engine now initiates compliance checks.
For
SIMPLE settlements, a single compliance check covers the entire settlement
(KYC, AML, sanctions screening). For
DVP /
PVP
settlements, each leg triggers an independent compliance check. The compliance request is dispatched to
compliance-api :8100 with zone-specific screening parameters.
settlement-api :8050
compliance-api :8100
compliance request dispatched
The compliance API generates zero-knowledge compliance proofs for each required check.
KYC verification, AML screening, and sanctions list evaluation run per compliance zone.
A compliance receipt hash is computed and stored for auditability.
For DVP/PVP: each leg is independently evaluated. Compliance is atomic - if
any leg
is denied, the entire settlement is rejected (one leg fails = whole settlement fails).
If any leg denied →
REJECTED.
If all pass (decision = "allow") →
ACCEPTED.
compliance-api :8100
proof-verifier :8250
decision = "allow"
Compliance checks have passed. What happens next depends on the settlement type:
SIMPLE: Auto-transition to READY immediately. No manual attestation needed - the engine
moves the settlement forward on the next poll tick.
DVP/PVP: The settlement enters a waiting state. Each counterparty must confirm
their leg via
POST /v1/settlements/:id/attest-ready. The engine holds
at ACCEPTED until all legs are attested. Once every leg has been attested →
READY.
settlement-api :8050
settlement-consumer :8051
all legs attested (or auto for SIMPLE)
All preconditions are satisfied. The settlement engine submits the transaction to JIL L1 via the
ledger-router :8000. An L1
tx_id is assigned
upon successful submission.
For DVP/PVP settlements, the primary leg is used for L1 submission. The ledger-router coordinates
multi-leg atomic execution on the L1 engine.
If L1 submission fails →
FAILED.
If succeeds →
SUBMITTED.
ledger-router :8000
ledger-service :8001
tx_id assigned, included in mempool
The transaction has been included in a JIL L1 block. The engine records the block height
(
block_height) and block hash (
block_hash).
A confirmation counter begins tracking subsequent blocks.
A submission timeout is enforced - if the transaction is not confirmed within the timeout window,
the settlement transitions to FAILED.
If timeout →
FAILED.
If block inclusion confirmed →
CONFIRMED.
jil5600-core (Rust L1)
settlement-consumer :8051
block confirmed, tracking confirmations
The block containing this settlement has been included in the chain. Confirmations accumulate
with each new block produced after the settlement's block. The finality threshold is
FINALITY_CONFIRMATIONS=6 (default).
The engine re-checks the confirmation count on each poll tick (
ENGINE_POLL_INTERVAL_MS).
Once the threshold is met →
FINAL.
finality-prover
settlement-consumer :8051
6 confirmations reached
Finality threshold met. The settlement engine performs the following final operations:
1. Fee calculated at finality (
SETTLEMENT_FEE_BPS=5, i.e. 3-5 bps).
2. Settlement receipt assembled with all proofs and metadata.
3. Finality proof stored in the proof store.
4. For DVP/PVP: all legs marked as "settled".
5. Webhook dispatched to the client's registered callback URL.
6. Kafka event published:
SETTLEMENT_FINAL.
7. Batch settlement counts updated.
settlement-api :8050
settlement-consumer :8051
proof-verifier :8250
03DVP/PVP Leg Attestation Flow
For multi-leg settlements (DVP and PVP), the engine holds at the ACCEPTED state until all counterparties have
confirmed their legs. This section details the attestation branching.
Settlement is compliance-approved. The engine creates attestation slots for each leg.
leg_1: { party: "counterparty_a", attested: false, asset: "USD" }
leg_2: { party: "counterparty_b", attested: false, asset: "jBTC" }
settlement-api :8050
counterparty_a calls attest-ready
Counterparty A has confirmed their leg. The engine updates the attestation record.
Settlement remains at ACCEPTED because not all legs are attested yet.
POST /v1/settlements/{id}/attest-ready
{ "leg_id": "leg_1", "party_id": "counterparty_a" }
settlement-api :8050
counterparty_b calls attest-ready
Both counterparties have attested their legs. On the next engine poll tick, the settlement
transitions from ACCEPTED to READY and proceeds to L1 submission.
For PVP: both legs are bundled into a single atomic L1 transaction.
For DVP: the primary leg is submitted first, with the secondary leg following once the primary is confirmed.
settlement-api :8050
ledger-router :8000
04Terminal Failure States
Settlements can exit the happy path and reach a terminal failure state at several points in the lifecycle.
The settlement has been permanently rejected due to a compliance failure. This is a terminal state - no recovery is possible.
Causes:
• Travel rule evaluation failed (missing or invalid party data)
• KYC check failed (unverified counterparty)
• AML screening flagged (suspicious activity pattern)
• Sanctions list match (OFAC, EU, UN)
• Any single leg denied in a DVP/PVP settlement (atomic rejection)
Can occur from: RECEIVED, VALIDATED, COMPLIANCE_CHECK
compliance-api :8100
settlement-api :8050
terminal - no recovery
The settlement failed during the execution phase. This is a terminal state.
Causes:
• L1 transaction submission rejected by ledger-router (insufficient balance, invalid tx)
• L1 submission timeout (transaction not included in a block within the window)
• Block confirmation timeout (transaction included but chain stalled)
• L1 engine internal error (jil5600-core error)
Can occur from: READY, SUBMITTED
ledger-router :8000
jil5600-core
terminal - no recovery
The client has explicitly cancelled the settlement via
POST /v1/settlements/:id/cancel.
Cancellation is only permitted while the settlement is in a pre-execution state.
Cancellable states: RECEIVED, VALIDATED, ACCEPTED, READY
Non-cancellable states: SUBMITTED, CONFIRMED, FINAL (already on-chain)
Client SDK
settlement-api :8050
terminal - client choice
The settlement has exceeded its expiry deadline (default:
SETTLEMENT_EXPIRY_MINUTES=60).
The engine checks expiry on each poll tick and automatically transitions stale settlements to EXPIRED.
Common causes:
• DVP/PVP settlement where one counterparty never attested their leg
• Compliance check took too long (external provider timeout)
• Client submitted but never completed follow-up actions
Can occur from: RECEIVED, VALIDATED, COMPLIANCE_CHECK, ACCEPTED
settlement-consumer :8051
05API Endpoints
| Method |
Endpoint |
Purpose |
Notes |
| POST |
/v1/settlements |
Create a new settlement |
Returns settlement UUID and UETR. Body includes type (SIMPLE/DVP/PVP), parties, amount, asset, legs. |
| POST |
/v1/settlements/batch |
Batch create settlements |
1-100 SIMPLE settlements per batch. Returns array of UUIDs. DVP/PVP not supported in batch. |
| POST |
/v1/settlements/:id/cancel |
Cancel a settlement |
Only valid in states: received, validated, accepted, ready. Returns 409 if already submitted. |
| POST |
/v1/settlements/:id/attest-ready |
DVP/PVP leg attestation |
Counterparty confirms their leg is ready. Body: { leg_id, party_id }. Idempotent. |
| GET |
/v1/settlements/:id |
Get settlement status |
Returns full settlement record including current state, legs, compliance receipt, timestamps. |
| GET |
/v1/settlements/:id/proof |
Get finality proof |
Only available when state = FINAL. Returns ZK compliance proof + L1 finality proof. |
| GET |
/v1/settlements/:id/receipt |
Get settlement receipt |
Only available when state = FINAL. Returns full receipt with fee, block height, finality data. |
Example: Create a SIMPLE Settlement
POST /v1/settlements
{
"type": "SIMPLE",
"asset": "USD",
"amount": "1000000.00",
"sender": {
"legal_name": "Acme Bank NA",
"account_id": "acct_sender_001",
"country": "US",
"address": "123 Wall St, New York, NY 10005"
},
"receiver": {
"legal_name": "Deutsche Finanz AG",
"account_id": "acct_receiver_002",
"country": "DE",
"address": "Taunusanlage 12, 60325 Frankfurt"
},
"callback_url": "https://api.acmebank.com/webhooks/settlement"
}
Example: Create a DVP Settlement
POST /v1/settlements
{
"type": "DVP",
"legs": [
{
"leg_id": "leg_1",
"direction": "delivery",
"asset": "USD",
"amount": "50000.00",
"party": { "party_id": "counterparty_a", "legal_name": "Fund Alpha LP" }
},
{
"leg_id": "leg_2",
"direction": "payment",
"asset": "jBTC",
"amount": "0.75000000",
"party": { "party_id": "counterparty_b", "legal_name": "OTC Desk Corp" }
}
],
"callback_url": "https://api.fundalpha.com/webhooks/settlement"
}
06Service Architecture
The settlement lifecycle is orchestrated across five services, each responsible for specific state transitions.
| Service |
Port |
Responsibility |
States Handled |
| settlement-api |
:8050 |
HTTP API, settlement creation, cancellation, attestation, receipt assembly, webhook dispatch |
All states (orchestration layer) |
| settlement-consumer |
:8051 |
Kafka consumer, engine poll loop, state transition executor, expiry checker, batch counter |
All state transitions (engine driver) |
| compliance-api |
:8100 |
ZK compliance proof generation, KYC/AML/sanctions screening, travel rule evaluation |
VALIDATED → COMPLIANCE_CHECK → ACCEPTED or REJECTED |
| ledger-router |
:8000 |
L1 transaction routing, submission to jil5600-core, block confirmation tracking |
READY → SUBMITTED → CONFIRMED |
| jil5600-core |
Rust L1 |
Block production, transaction execution, consensus, finality determination |
SUBMITTED → CONFIRMED → FINAL (block-level) |
07Database Schema
settlements
id UUID PRIMARY KEY
uetr UUID NOT NULL UNIQUE
type TEXT NOT NULL
status TEXT NOT NULL
asset TEXT NOT NULL
amount NUMERIC NOT NULL
sender_legal_name TEXT
sender_account_id TEXT NOT NULL
sender_country TEXT
sender_address TEXT
receiver_legal_name TEXT
receiver_account_id TEXT NOT NULL
receiver_country TEXT
receiver_address TEXT
compliance_receipt TEXT
compliance_decision TEXT
travel_rule_result TEXT
tx_id TEXT
block_height BIGINT
block_hash TEXT
confirmations INT DEFAULT 0
fee_bps INT
fee_amount NUMERIC
finality_proof TEXT
callback_url TEXT
batch_id UUID
expires_at TIMESTAMPTZ NOT NULL
created_at TIMESTAMPTZ DEFAULT NOW()
updated_at TIMESTAMPTZ DEFAULT NOW()
settlement_legs
id UUID PRIMARY KEY
settlement_id UUID NOT NULL REFERENCES settlements(id)
leg_id TEXT NOT NULL
direction TEXT NOT NULL
asset TEXT NOT NULL
amount NUMERIC NOT NULL
party_id TEXT NOT NULL
party_legal_name TEXT
compliance_receipt TEXT
compliance_decision TEXT
attested BOOLEAN DEFAULT FALSE
attested_at TIMESTAMPTZ
status TEXT DEFAULT 'pending'
created_at TIMESTAMPTZ DEFAULT NOW()
settlement_events
id BIGSERIAL PRIMARY KEY
settlement_id UUID NOT NULL REFERENCES settlements(id)
event_type TEXT NOT NULL
from_state TEXT
to_state TEXT
metadata JSONB
created_at TIMESTAMPTZ DEFAULT NOW()
08Configuration
Key configuration parameters for the settlement engine. All values are set via environment variables.
FINALITY_CONFIRMATIONS
6
Number of L1 block confirmations required before a settlement reaches FINAL state.
ENGINE_POLL_INTERVAL_MS
2000
Poll interval (ms) for the settlement engine tick loop. Each tick checks for pending state transitions and expiry.
SETTLEMENT_FEE_BPS
5
Settlement fee in basis points (0.05%). Applied at finality. Effective range: 3-5 bps depending on volume tier.
SETTLEMENT_EXPIRY_MINUTES
60
Default expiry deadline (minutes) for new settlements. Settlements not finalized within this window are automatically expired.
| Variable |
Service |
Default |
Purpose |
| FINALITY_CONFIRMATIONS |
settlement-consumer |
6 |
L1 block confirmations for finality threshold |
| ENGINE_POLL_INTERVAL_MS |
settlement-consumer |
2000 |
Engine tick loop interval in milliseconds |
| SETTLEMENT_FEE_BPS |
settlement-api |
5 |
Fee basis points applied at finality |
| SETTLEMENT_EXPIRY_MINUTES |
settlement-api |
60 |
Settlement expiry deadline in minutes |
| SETTLEMENT_BATCH_MAX |
settlement-api |
100 |
Maximum settlements per batch request |
| COMPLIANCE_API_URL |
settlement-api |
http://compliance-api:8100 |
Compliance API base URL |
| LEDGER_ROUTER_URL |
settlement-api |
http://ledger-router:8000 |
Ledger router base URL for L1 submission |
| KAFKA_BROKERS |
settlement-consumer |
redpanda:9092 |
Kafka/RedPanda broker addresses |
| WEBHOOK_TIMEOUT_MS |
settlement-api |
5000 |
Timeout for webhook delivery attempts |
| WEBHOOK_MAX_RETRIES |
settlement-api |
3 |
Maximum webhook delivery retry attempts |
09Key Source Files
| File |
Purpose |
| services/settlement-api/src/index.ts |
Settlement API server - HTTP endpoints, create/cancel/attest-ready handlers |
| services/settlement-api/src/engine.ts |
Settlement state machine - state transition logic, expiry checks, fee calculation |
| services/settlement-api/src/compliance.ts |
Compliance integration - dispatches checks to compliance-api, processes results |
| services/settlement-api/src/travel-rule.ts |
Travel rule evaluation - FATF Wire Transfer Rule party data validation |
| services/settlement-consumer/src/index.ts |
Kafka consumer - engine poll loop, processes settlement.* topic events |
| services/compliance-api/src/index.ts |
Compliance API - ZK proof generation, KYC/AML/sanctions screening |
| services/ledger-router/src/index.ts |
Ledger router - L1 transaction submission, block confirmation tracking |
| services/settlement-api/migrations/001_settlements.sql |
Settlement tables schema - settlements, settlement_legs, settlement_events |
| docs/openapi/settlement-router.yaml |
OpenAPI specification for settlement API endpoints |