1What this is
A digital twin of the financial ecosystem: simulated counterparts of every institution a payment or financial application must talk to in the Dominican Republic, organized exactly as the Central Bank organizes the real thing.
Each counterpart speaks the real message format of its kind, keeps state - accounts, balances, orders, cases - obeys the rules the Reglamento sets, and can be put into failure modes. Your team plugs its application in and runs an entire process end to end, from onboarding to settlement in the simulated LBTR, without touching a real institution, a real person or real money. Every message is recorded and the run produces an instrumented report.
The value is not any single simulator. It is that all of them exist together, consistently, in one place, with one synthetic population moving through them, so a process that crosses six organizations can be exercised as one process under the rules that will apply when it is real.
This is NOT an «ambiente de prueba» under Art. 83 of the Reglamento de Sistemas de Pago. The ecosystem involves no real providers and no real external users, so it does not fall under the no-objection regime. A run is never a substitute for the Central Bank's no objection. Where your next step is an Art. 83 request, a run report is evidence for that request and nothing more.
What is live today
This instance runs the whole of Phase 1 and the typed contracts of Phase 2. It holds 76 catalogued rules, 12 scenarios covering 32 runs with their variants, and 46 documented endpoints.
| Phase | What it covers | State |
|---|---|---|
| Phase 0 | Actor catalogue, message contracts, scenario and rule formats, synthetic population, naming and disclaimers. Three of the four BCRD instructivos read page by page and adopted into the catalogue. | complete |
| Phase 1 | Central Bank with current accounts, LBTR with its queue, priority and prelation; SGPI at stateful fidelity; Pagos al Instante; three banks with three personalities; an electronic-payment entity; the DD/DC administrator; identity and a credit bureau; the ATM network and the machine lane; the behavioural tier; a scored adversarial detection; the console; per-team worlds. | live |
| Phase 2 | Acquirer and sub-acquirer at stateful tier, the packed ISO 8583 wire codec, card clearing and chargebacks, wallet and initiation providers, agents at stateful tier, the simulated supervisor and UAF, chaos injectors. | typed contracts only |
| Phase 3 | Cheques and the SCC, insurers and brokers, remittance and exchange agents, SIPA, telcos and the tax authority, the rest of the adversarial catalogue. | not started |
| Phase 4 | Public catalogue and self-service keys, sponsored nodes, regulator and academic access. | this manual is the first part of it |
2Quick start
Five minutes: get a key, ask whether the instance is up, read your world, send a payment, open the run it produced.
1. Get a team key
A key is issued to a team, not to a person. Ask for one through the
key-request form, or write to partners@cemi.ai.
The Lab hands you a string beginning lab-. Put it in an environment variable and keep it
out of your repository, your deploy commands and your shell history.
2. Everything else
# 1. the key never appears in a command, a file or a shell history
export FINLAB_API_KEY="<the key the Lab gave your team>"
export FINLAB="https://ecosystem.financial"
# 2. is it up, and what is it running?
curl -s "$FINLAB/v1/health" | jq
# 3. what is in your world?
curl -s -H "x-lab-api-key: $FINLAB_API_KEY" "$FINLAB/v1/directory" | jq '{
institutions: [.institutions[] | select(.role=="bank") | .id],
aliases: [.aliases[0:3][] | .alias],
firstPerson: .people[0]
}'
# 4. send an instant payment, addressed by alias, in ISO 20022 shape
curl -s -X POST "$FINLAB/v1/payments/instant" \
-H "x-lab-api-key: $FINLAB_API_KEY" -H 'content-type: application/json' \
-d '{
"GrpHdr": { "MsgId": "MSG-0001" },
"CdtTrfTxInf": [{
"PmtId": { "EndToEndId": "E2E-0001" },
"Amt": { "InstdAmt": { "Ccy": "DOP", "value": 1500.00 } },
"DbtrAgt": { "FinInstnId": { "Othr": { "Id": "banco-norte" } } },
"DbtrAcct": { "Id": { "Othr": { "Id": "<an account number from the directory>" } } },
"CdtrAlias": "<an alias from the directory>"
}]
}' | jq
# 5. run a scenario end to end, and open its report
curl -s -X POST "$FINLAB/v1/runs" \
-H "x-lab-api-key: $FINLAB_API_KEY" -H 'content-type: application/json' \
-d '{ "scenario": "sgpi-instant-alias" }' | jq
curl -s -H "x-lab-api-key: $FINLAB_API_KEY" "$FINLAB/v1/runs" | jq '.[0]'// Node 18+ or any modern browser runtime. No SDK: it is plain HTTP.
const BASE = process.env.FINLAB ?? 'https://ecosystem.financial';
const KEY = process.env.FINLAB_API_KEY; // never hard-code the key
const headers = { 'x-lab-api-key': KEY, 'content-type': 'application/json' };
async function call(path, init = {}) {
const res = await fetch(BASE + path, { ...init, headers: { ...headers, ...(init.headers ?? {}) } });
const body = await res.json();
if (!res.ok) throw Object.assign(new Error(body.message ?? res.statusText), { status: res.status, body });
return body;
}
const health = await call('/v1/health');
console.log(health.status, 'rule catalogue', health.ruleCatalogue);
// everything your team's world holds
const directory = await call('/v1/directory');
const payer = directory.people.find((p) => p.accounts.some((a) => a.kind === 'sight'));
const payee = directory.aliases.find((a) => a.holder !== payer.fullName);
// an instant payment, addressed by alias
const result = await call('/v1/payments/instant', {
method: 'POST',
body: JSON.stringify({
GrpHdr: { MsgId: 'MSG-0001' },
CdtTrfTxInf: [{
PmtId: { EndToEndId: 'E2E-' + Date.now() },
Amt: { InstdAmt: { Ccy: 'DOP', value: 1500.0 } },
DbtrAgt: { FinInstnId: { Othr: { Id: payer.accounts[0].institution } } },
DbtrAcct: { Id: { Othr: { Id: payer.accounts[0].accountNumber } } },
CdtrAlias: payee.alias,
}],
}),
});
// the findings are the point: every rule the payment touched, with its article
for (const finding of result.findings) {
console.log(finding.status.toUpperCase(), finding.ruleId, '-', finding.article, '-', finding.detail);
}# Python 3.9+, standard library only. No SDK: it is plain HTTP.
import json, os, urllib.request
BASE = os.environ.get("FINLAB", "https://ecosystem.financial")
KEY = os.environ["FINLAB_API_KEY"] # never hard-code the key
def call(path, body=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(
BASE + path,
data=data,
headers={"x-lab-api-key": KEY, "content-type": "application/json"},
method="POST" if data else "GET",
)
try:
with urllib.request.urlopen(req) as res:
return json.load(res)
except urllib.error.HTTPError as err: # a refusal is an answer, so read it
raise SystemExit(json.load(err)) from None
health = call("/v1/health")
print(health["status"], "rule catalogue", health["ruleCatalogue"])
directory = call("/v1/directory")
payer = next(p for p in directory["people"] if p["accounts"])
payee = next(a for a in directory["aliases"] if a["holder"] != payer["fullName"])
result = call("/v1/payments/instant", {
"GrpHdr": {"MsgId": "MSG-0001"},
"CdtTrfTxInf": [{
"PmtId": {"EndToEndId": "E2E-0001"},
"Amt": {"InstdAmt": {"Ccy": "DOP", "value": 1500.00}},
"DbtrAgt": {"FinInstnId": {"Othr": {"Id": payer["accounts"][0]["institution"]}}},
"DbtrAcct": {"Id": {"Othr": {"Id": payer["accounts"][0]["accountNumber"]}}},
"CdtrAlias": payee["alias"],
}],
})
for finding in result["findings"]:
print(finding["status"].upper(), finding["ruleId"], "-", finding["article"], "-", finding["detail"])What just happened
/v1/healthneeds no credential at all, because a probe that needs a credential is not a probe. It tells you the rule-catalogue version the instance is running, which is the version every finding you get back is judged against./v1/directoryis your team's world: institutions, machines, aliases, sample people with their synthetic documents and their accounts. Another team asking the same question gets entirely different people.- The payment was instructed in ISO 20022 shape - a faithful JSON projection of pain.001 - and came back with its findings: every rule it touched, each one carrying the article behind it.
- The run report is the record. It carries the seed, so anybody holding it can reproduce the run exactly.
3Authentication
Two credentials, and they identify two different kinds of thing. A team key identifies a team. A signed-in principal identifies a person.
The team key
Sent in the x-lab-api-key header on every call. It carries the team it belongs to, and the team
decides which world the request reaches - so a key opens exactly one ecosystem and can never observe, move or
reach anything belonging to another team.
A key opens the whole of /v1, including the machine lane at /v1/atm/**, where it is
the only credential that can possibly work: an ATM cannot hold a session. It may also start runs, because a run
happens in the team's own world and its report is written under the team and visible to nobody else.
x-lab-api-key: lab-3f9a...
Signing in to the console
The console at ecosystem.financial signs in with Firebase Auth - Google, or an email and a password -
and sends the ID token it is given in an Authorization: Bearer header. The gateway verifies it on
every call. Which team a person belongs to is configured on the instance; a person the instance does not map
falls into the default team.
What each one may do
| Team key | Signed-in person | Admin principal | |
|---|---|---|---|
| Read the directory, the actors, the rules | its own world | its own world | any team's, with x-lab-team |
| Drive payments and the machine lane | yes | yes | yes |
| Advance the clock | its own world only | its own world only | any team's |
| Start a run | yes | if on the allow-list | yes |
| List and open run reports | its own team's | its own team's | every team's |
| Reset a world | its own | its own, if on the allow-list | any team's |
ALLOWED_PRINCIPALS decides which signed-in people may spend the Lab's compute by starting a run. ADMIN_PRINCIPALS decides who sees every team. Neither of them governs keys, because a key is already confined to one world.Rotation and revocation
Keys live in Secret Manager and never in this repository, a deploy command or a shell history. Rotating your team's key means the Lab writes a new secret version with your old key replaced; the new key works on the next revision and the old one stops. Revoking means a version without your pair. Neither touches your run reports, which are the record.
Ask for a rotation through the feedback form or at security@cemi.ai. If you believe a key has leaked, say so immediately and do not wait to be sure - a rotation costs the Lab a minute.
4Endpoint reference
Generated from the instance's own OpenAPI document, so it cannot drift from what the gateway
actually answers. The live contract is at /openapi.json and is the same
document.
Error shapes
Three things can go wrong, and they are deliberately different answers.
| Status | Shape | What it means |
|---|---|---|
401 | { error, message } | No credential, or one that did not verify. The message says which credential the lane expects. |
403 | { error, message } | A valid credential that may not do this: a person not on the allow-list starting a run, or a team asking to act as another team. |
404 | { error, message } | No such endpoint, or no such run that you may see. Another team's report answers 404, the same as one that does not exist, so a listing is not enumerable from outside. |
422 | { error, message, findings[] } | A refusal. The ecosystem understood you and said no - a cap breached, a beneficiary charged, an order past its window. error is the actor's own code and findings carries every rule the attempt touched, with its article. |
CHARGE_TO_BENEFICIARY_REFUSED and a failing TRANSFER.NO_CHARGE_TO_BENEFICIARY finding citing Art. 81 is the ecosystem doing its job. On the machine lane the rule is different again: an issuer decline comes back 200 with approved: false and its ISO 8583 DE039 code, because that is what a machine expects - and because the declines are what makes a card-testing pattern detectable at all.Findings: what comes back with every call
Every operation returns the rules it touched alongside its result. A finding names the rule, the article, the
instrument and the resolution date, what was observed and what was expected, and when. Findings with
needsRecheck come from an instructivo that cites a superseded Reglamento - the rule is live, it is
just not presented as settled.
{
"result": { "...": "the operation's own answer" },
"findings": [
{
"ruleId": "SGPI.FINAL_CREDIT",
"article": "Art. 62",
"instrument": "Reglamento de Sistemas de Pago",
"resolution": "Segunda Resolucion de la Junta Monetaria",
"resolutionDate": "2025-08-28",
"status": "pass",
"severity": "critical",
"actor": "central-bank",
"observed": 1700,
"expected": "<= 10s",
"detail": "elapsed 1.7s against a limit of 10s (Art. 62)",
"at": "2026-09-08T13:00:01.700Z",
"local": "2026-09-08 09:00"
}
]
}
ATM and ITM
GET /v1/atm/terminals The machines registered on the simulated ATM network
Each terminal names the participant that operates it and holds its notes, the currency it dispenses, and the notes left in its cassettes.
Credential: x-lab-api-key (team key)
Request body
No request body.
Responses
200 | the terminals |
POST /v1/atm/withdrawals Cardholder withdrawal at a machine
The switch routes the transaction to the issuer, which verifies the holder by PIN or by biometric through the simulated identity entity, authorizes, and lets the machine's operator pay out the notes. The position between issuer and operator settles net in the simulated LBTR at the end of the cycle (Art. 79). An issuer decline comes back as a 200 with approved: false and its ISO 8583 DE039 response code - a decline is a response, not an error, which is what a machine expects.
Credential: x-lab-api-key (team key)
Request body
{
"$ref": "#/components/schemas/AtmWithdrawalRequest"
}
Responses
200 | the machine transaction, approved or declined |
422 | the transaction could not be attempted; the body names the rule and its article |
POST /v1/atm/cardless/orders Start a cardless (phygital) withdrawal on the phone
The holder's own institution checks the account and asks the ATM-network administrator to mint a one-time code and the QR that carries the data needed to initiate the order (Art. 78 numeral IV). The order is single-use and expires; both controls are the Lab's and are catalogued as such.
Credential: x-lab-api-key (team key)
Request body
{
"$ref": "#/components/schemas/CardlessOrderRequest"
}
Responses
200 | the order, its code and its QR payload |
422 | refused |
POST /v1/atm/cardless/redemptions Finish a cardless withdrawal at the machine
The machine sends the code the holder keyed in, or the QR it read. The order is spent exactly once.
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"properties": {
"terminalId": {
"type": "string"
},
"code": {
"type": "string"
},
"qr": {
"type": "string"
}
},
"required": [
"terminalId"
]
}
Responses
200 | the machine transaction |
422 | the order is unknown, expired or already redeemed |
POST /v1/atm/deposits Deposit at the machine, with note recognition
What the machine recognizes is what is credited. A difference between the declared and the recognized amount is recorded as an adjustment, which Art. 41 requires of the ten-year record.
Credential: x-lab-api-key (team key)
Request body
{
"$ref": "#/components/schemas/AtmDepositRequest"
}
Responses
200 | the machine transaction |
422 | refused |
POST /v1/atm/remittance-payouts Pay out a remittance in cash at the machine
Against a balance the beneficiary's institution already holds. The beneficiary is never charged for it (Art. 81).
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"properties": {
"terminalId": {
"type": "string"
},
"issuerId": {
"type": "string"
},
"accountNumber": {
"type": "string"
},
"amountMinor": {
"type": "integer"
},
"remittanceRef": {
"type": "string"
},
"biometric": {
"type": "boolean"
}
},
"required": [
"terminalId",
"issuerId",
"accountNumber",
"amountMinor"
]
}
Responses
200 | the machine transaction |
422 | refused |
POST /v1/atm/balance-inquiries Balance inquiry at a machine
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"properties": {
"terminalId": {
"type": "string"
},
"issuerId": {
"type": "string"
},
"accountNumber": {
"type": "string"
}
},
"required": [
"terminalId",
"issuerId",
"accountNumber"
]
}
Responses
200 | the balance |
POST /v1/atm/echo Network management echo (ISO 8583 0800/0810)
What a machine sends to prove the switch is alive.
Credential: x-lab-api-key (team key)
Request body
No request body.
Responses
200 | the echo response |
POST /v1/atm/cycles Settle the network cycle
Art. 79: the net result of the cycle settles in the simulated LBTR. Issuers pay the operators of the machines their cardholders used.
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"properties": {
"cycleId": {
"type": "string"
}
}
}
Responses
200 | the cycle, its net positions and the LBTR legs that settled it |
La Poblacion del Lab
GET /v1/directory La Poblacion del Lab: your team's world, in one call
The starting point. Every institution standing in your world with its Lab name and its personality, the machines on the floor, the aliases registered in the simulated SGPI directory, sample people with their synthetic documents and their accounts (in full and masked), the businesses, the population counts and how many people the Lab has flagged as adversaries. It also carries the identifier scheme, so nobody has to look up why a document begins with LAB- before deciding whether it is safe to put in a slide.
Scoped to your own team. Two teams on the same instance are handed two different populations, because their worlds are seeded from their team ids and share nothing.
Credential: x-lab-api-key (team key) or Authorization: Bearer (signed-in principal)
Request body
No request body.
Responses
200 | the team's world: institutions, terminals, aliases, people, businesses |
POST /v1/worlds/reset Throw your team's world away and stand a fresh one up
Everything the team did is gone: accounts, balances, aliases, the LBTR queue, the clock. What comes back is a new world with the same starter population, built from the same seed, so the people and their documents are the ones the directory listed before.
The run reports the team already produced are NOT gone. A report is the record, and a reset is not a way to unsay one.
Resets your own team's world. An admin principal may reset another team's by sending x-lab-team; nobody else can.
Credential: x-lab-api-key (team key) or Authorization: Bearer (signed-in principal)
Request body
No request body.
Responses
200 | the fresh world: its seed, its instant, its actors and its population |
403 | this principal may not reset a world on this instance |
GET /v1/worlds Which worlds are standing on this instance
A team is told its own. An admin principal is told every world that has been stood up, and when.
Credential: x-lab-api-key (team key) or Authorization: Bearer (signed-in principal)
Request body
No request body.
Responses
200 | the worlds this principal may see |
Other
GET /v1/health Liveness, the simulated clock, the reports backend and whether sign-in is required
Exempt from the team key and from sign-in: a probe that needs a credential is not a probe. Also answers on /health and, locally, on /healthz - Google's front end intercepts that last path before it reaches a Cloud Run container, which is why there are three.
Credential: none - open, so a probe never needs a credential
Request body
No request body.
Responses
200 | the gateway is up |
GET /health Liveness (alias of /v1/health)
Credential: none - open, so a probe never needs a credential
Request body
No request body.
Responses
200 | the gateway is up |
GET /healthz Liveness (alias of /v1/health; intercepted by Google's front end on Cloud Run)
Credential: none - open, so a probe never needs a credential
Request body
No request body.
Responses
200 | the gateway is up |
GET /v1/actors The simulated counterparts on the bus, with their fidelity tier and the ops each answers
Credential: x-lab-api-key (team key)
Request body
No request body.
Responses
200 | the actor catalogue |
GET /v1/rules The rule catalogue: every executable rule with its article and the resolution that fixed its value
Credential: x-lab-api-key (team key)
Request body
No request body.
Responses
200 | the rule catalogue |
GET /v1/findings Rule findings raised so far in this session
Credential: x-lab-api-key (team key)
Request body
No request body.
Responses
200 | findings |
GET /v1/trace The message trace of this session
Credential: x-lab-api-key (team key)
Request body
No request body.
Responses
200 | trace entries |
POST /v1/clock/advance Advance the simulated clock (stepped and accelerated modes)
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"properties": {
"by": {
"type": "string",
"example": "8m"
}
},
"required": [
"by"
]
}
Responses
200 | the new simulated instant |
POST /v1/payments/instant SGPI instant payment, ISO 20022 pacs.008 projection in, pacs.002 out
Final credit within ten seconds (Art. 60-61), 24/7/365, alias addressing. A charge bearer of CRED is refused: the beneficiary of a transfer is never charged (Art. 81; Art. 56 numeral II).
Credential: x-lab-api-key (team key)
Request body
{
"$ref": "#/components/schemas/CreditTransferInstruction"
}
Responses
200 | pacs.002 payment status report |
422 | the instruction was refused; the body names the rule and its article |
POST /v1/payments/pai Pagos al Instante BCRD transfer
Final credit within eight minutes, 07:00-23:00 local with a weekday pause 16:00-18:30. An order given OUTSIDE that schedule is not refused: it becomes effective at 08:00 on the NEXT BUSINESS DAY, so a Friday-night order lands on Monday morning. A dollar transfer needs no correspondent - it settles in the LBTR, which settles in Dominican pesos, US dollars and euros (Art. 56) - and where the beneficiary account is in another currency, named on CdtrAcct.Ccy, the originating institution converts and the conversion is recorded with the transaction (Arts. 41-42). The rates are Lab indicative figures, never quotations.
Credential: x-lab-api-key (team key)
Request body
{
"$ref": "#/components/schemas/CreditTransferInstruction"
}
Responses
200 | pacs.002 payment status report |
422 | refused |
POST /v1/transfers/lbtr High-value transfer through the LBTR
Gross settlement in DOP, USD or EUR. An order that the ordering participant cannot cover queues and is revocable while queued (Art. 66); the third-party credit follows within four minutes (Art. 57).
Credential: x-lab-api-key (team key)
Request body
{
"$ref": "#/components/schemas/CreditTransferInstruction"
}
Responses
200 | the LBTR order and its status |
POST /v1/webhooks Register a webhook for bus notifications
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"properties": {
"url": {
"type": "string"
},
"events": {
"type": "array",
"items": {
"type": "string"
}
}
},
"required": [
"url"
]
}
Responses
201 | the subscription |
POST /v1/actors/central-bank/ops/{op} Invoke an operation on Banco Central del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83.
Operations: registerParticipant, openCurrentAccount, creditCurrentAccount, currentAccountBalance, issueAccountNumber, validateAccountNumber, lbtr.submit, lbtr.revoke, lbtr.status, lbtr.queue, lbtr.endOfDayFlush, sgpi.registerAlias, sgpi.resolveAlias, sgpi.pay, pai.submit, pai.raiseClaim, pai.answerClaim, pai.claims, net.settle, vigilancia.report
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/bank/ops/{op} Invoke an operation on Banco del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83.
Operations: onboardCustomer, openAccount, deposit, balance, accountInfo, internalTransfer, transfer, registerAlias, postIncomingCredit, ddc.debitOriginator, ddc.reverse, lbtr.creditNotice, lbtr.revocationNotice, lbtr.revoked, sgpi.notification, cb.currentAccountCredited, atm.withdraw, records.query, records.attest, customer, issueCard, card, cards, atm.authorizeWithdrawal, atm.postDeposit, atm.terminalCashOut, atm.terminalCashIn, cardless.request, monitoring.feed, monitoring.resolveCard
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/epe/ops/{op} Invoke an operation on EPE del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83.
Operations: openEPaymentAccount, agentOperation, balance, accountInfo, addCredential, inboundRemittance, socialSubsidy, chequeFunding, registerAlias, postIncomingCredit, reconcileFloat, dailyReport, headroom, raiseClaim, answerClaim, claims, sgpi.notification, cb.currentAccountCredited, agents, atm.authorizeWithdrawal, atm.postDeposit, monitoring.feed
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/ddc-administrator/ops/{op} Invoke an operation on Administrador de Debito y Credito Directo del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83.
Operations: submitBatch, runCycle, batch, cycles, reasonCodes
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/identity/ops/{op} Invoke an operation on Registro e Identidad del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83.
Operations: lookup, verify
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/credit-bureau/ops/{op} Invoke an operation on Buro de Credito del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83.
Operations: inquiry, history
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/monitoring/ops/{op} Invoke an operation on Unidad de Monitoreo del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83.
Operations: run, alerts, score, movements, thresholds
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/acquirer/ops/{op} Invoke an operation on Adquirente del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83. Stub tier: canned responses, no state.
Operations: affiliateMerchant, authorize, capture, refund, chargeback, settlementReport
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/sub-acquirer/ops/{op} Invoke an operation on Subadquirente del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83. Stub tier: canned responses, no state.
Operations: onboardSubMerchant, authorize, payout
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/card-processor/ops/{op} Invoke an operation on Procesador card-processor del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83. Stub tier: canned responses, no state.
Operations: authorize, clearing, settlement, tokenize, threeDSecure, iso8583
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/atm-network-administrator/ops/{op} Invoke an operation on Administrador de Red de Cajeros del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83.
Operations: registerTerminal, terminals, terminal, withdraw, cardless.order, cardless.redeem, cardless.status, cardless.cancel, deposit, remittancePayout, balanceInquiry, networkEcho, settleCycle, transactions, positions
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/wallet-provider/ops/{op} Invoke an operation on Billetera del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83. Stub tier: canned responses, no state.
Operations: enrolInstrument, presentQr, readQr
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/initiation-provider/ops/{op} Invoke an operation on Iniciador de Pagos del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83. Stub tier: canned responses, no state.
Operations: requestConsent, initiate
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/gateway-provider/ops/{op} Invoke an operation on Pasarela del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83. Stub tier: canned responses, no state.
Operations: createCheckout, webhook
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/supervisor/ops/{op} Invoke an operation on Superintendencia del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83. Stub tier: canned responses, no state.
Operations: submitReport, requestRecords
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
POST /v1/actors/uaf/ops/{op} Invoke an operation on Unidad de Analisis Financiero del Lab
Simulated counterpart operated by the CEMI Financial Innovation Lab. Not a real institution, not endorsed by the entity it is modelled after, and not an ambiente de prueba under Art. 83. Stub tier: canned responses, no state.
Operations: suspiciousTransactionReport, cashTransactionReport
Credential: x-lab-api-key (team key)
Request body
{
"type": "object",
"additionalProperties": true
}
Responses
200 | the operation result |
422 | the operation was refused |
Runs and reports
GET /v1/principal Who this gateway thinks you are, which team you are, and what you may do
A team key answers as kind: key and carries the team the key belongs to; a console sign-in answers as kind: user with the verified email and the team TEAM_PRINCIPALS maps it to. mayStartRuns reflects this instance's ALLOWED_PRINCIPALS list for a person, and is always true for a team key, because a key can only ever write into its own world. admin is true for a principal on ADMIN_PRINCIPALS, who sees every team's runs and may act on any team's world with the x-lab-team header.
Credential: x-lab-api-key (team key) or Authorization: Bearer (signed-in principal)
Request body
No request body.
Responses
200 | the principal |
GET /v1/scenarios The scenarios in this instance, with their variants
Credential: x-lab-api-key (team key) or Authorization: Bearer (signed-in principal)
Request body
No request body.
Responses
200 | the scenario catalogue |
GET /v1/runs The run reports YOUR TEAM holds, newest first
Scoped to the caller: a team sees its own runs and nobody else's, and an admin principal sees every team's. A report id carries its team in front of the run id, so asking for another team's report by id answers 404 - the same answer as for a report that does not exist, which is what stops the listing being enumerable from outside.
Backed by REPORTS_BACKEND: reports on disk, which is what makes a run reproducible by anybody holding the file, or in the project's Firestore, which is what a Cloud Run instance needs because it loses its filesystem on restart. The answer is the same either way.
Credential: x-lab-api-key (team key) or Authorization: Bearer (signed-in principal)
Request body
No request body.
Responses
200 | one summary row per run |
POST /v1/runs Run a scenario and keep the report
Requires a signed-in principal on the instance's allow-list when AUTH_REQUIRED is on.
Credential: x-lab-api-key (team key) or Authorization: Bearer (signed-in principal)
Request body
{
"type": "object",
"properties": {
"scenario": {
"type": "string"
},
"variant": {
"type": "string"
},
"seed": {
"type": "string"
}
},
"required": [
"scenario"
]
}
Responses
201 | the run started, finished and was kept |
403 | the principal may not start runs on this instance |
422 | the run could not be started |
GET /v1/runs/{id} One run report in full, with its trace, ledgers and reconciliation
Credential: x-lab-api-key (team key) or Authorization: Bearer (signed-in principal)
Request body
No request body.
Responses
200 | the report |
404 | no report with that id |
GET /v1/runs/{id}/report.md The same run report as rendered Markdown
The document a team attaches to an Art. 83 request as evidence, and nothing more.
Credential: x-lab-api-key (team key) or Authorization: Bearer (signed-in principal)
Request body
No request body.
Responses
200 | the report |
404 | no report with that id |
5Web services and the machine lane
Four interfaces, chosen so that a team integrates the way it would integrate for real.
| Interface | Where | What speaks it |
|---|---|---|
| REST + OpenAPI | everything under /v1 | an application, a test suite, a CI job |
| ISO 20022, JSON projection | /v1/payments/*, /v1/transfers/lbtr | a payments application instructing pain.001 and reading pacs.002 |
| ISO 8583, JSON projection | /v1/atm/** | an ATM or ITM integration; every response carries the 0200/0210 pair the switch built |
| Webhooks | POST /v1/webhooks | anything that would rather be told than poll |
Integrating a machine, step by step
This is the walkthrough for putting a real ATM or ITM - or a simulator of one - on the lane.
- Hold a team key on the device. The machine lane is the one lane where a key is not a convenience: hardware cannot hold a session, so the key is the credential. Provision it the way you would provision any device secret.
- Echo first.
POST /v1/atm/echois what a switch sends before it trusts a link. It costs nothing and it tells you the network is reachable and the key is good. - Read the terminals.
GET /v1/atm/terminalslists the machines standing in your world, their cassettes, their operator institution and whether they accept deposits. One of the four is deliberately out of cash, so your out-of-service path has something to exercise against. - Register your own terminal if you are driving real hardware:
POST /v1/actors/atm-network/ops/registerTerminalwith your terminal id, its operator, its cassette and whether it accepts deposits. - Drive a cardholder withdrawal. The PAN and the PIN are returned once at issuance and never
leave the issuer: the switch sees a masked PAN and the issuer's verdict. Read
approved, and on a refusal read the DE039 response code rather than treating it as an error. - Drive the cardless flow if you support it. The order is raised on the phone at the holder's
own institution and finished at the machine, exactly once and inside its validity. The QR payload is the Lab
string
LAB-QR:v1:<network>:<orderId>:<code>- the Central Bank's QR instructivo is not published, and the Lab does not invent one. - Close the cycle. The network holds no money and keeps no book: it is an indirect
participant.
POST /v1/atm/cyclessettles the day's net position in the simulated LBTR under Art. 79, and the report shows the gross legs it took to get there.
# The machine lane is what an ATM or ITM integration drives. It is the one lane where a
# team key is not a convenience but the only possible credential: hardware cannot hold a
# session, so it presents a key and nothing else.
export FINLAB="https://ecosystem.financial"
K="x-lab-api-key: $FINLAB_API_KEY"
# 0. the machines standing in your world, with their cassettes and their operator
curl -s -H "$K" "$FINLAB/v1/atm/terminals" | jq
# 1. a network echo, which is what a switch sends before it trusts a link
curl -s -X POST -H "$K" -H 'content-type: application/json' "$FINLAB/v1/atm/echo" -d '{}' | jq
# 2. a cardholder withdrawal. The PAN and the PIN come from the issuer at issuance and
# never leave it: the switch sees a masked PAN and the issuer's verdict, nothing more.
curl -s -X POST -H "$K" -H 'content-type: application/json' "$FINLAB/v1/atm/withdrawals" -d '{
"terminalId": "TERM-LAB-0001",
"labPan": "<the PAN issued to the card>",
"pin": "<the PIN returned once at issuance>",
"amountMinor": 200000,
"currency": "DOP"
}' | jq '{approved, responseCode, iso8583: .result.iso8583.DE039}'
# A DECLINE IS A RESPONSE, NOT AN ERROR. An issuer refusal comes back 200 with
# approved:false and its ISO 8583 DE039 code, because that is what a machine expects -
# and because the declines are what makes a card-testing pattern detectable at all.
# 3. the cardless flow: the order is raised on the phone, at the holder's own institution
ORDER=$(curl -s -X POST -H "$K" -H 'content-type: application/json' "$FINLAB/v1/atm/cardless/orders" -d '{
"issuerId": "banco-norte",
"accountNumber": "<an account number from the directory>",
"amountMinor": 300000
}')
echo "$ORDER" | jq '{orderId: .result.orderId, code: .result.code, qr: .result.qr}'
# 4. and finished at the machine, exactly once and inside its validity
curl -s -X POST -H "$K" -H 'content-type: application/json' "$FINLAB/v1/atm/cardless/redemptions" -d '{
"terminalId": "TERM-LAB-0002",
"code": "<the one-time code from step 3>"
}' | jq
# 5. a deposit the machine counted and recognized
curl -s -X POST -H "$K" -H 'content-type: application/json' "$FINLAB/v1/atm/deposits" -d '{
"terminalId": "TERM-LAB-0002",
"labPan": "<the PAN>",
"pin": "<the PIN>",
"amountMinor": 500000
}' | jq
# 6. close the cycle: the network holds no money and keeps no book, so the day's net
# position settles in the simulated LBTR (Art. 79)
curl -s -X POST -H "$K" -H 'content-type: application/json' "$FINLAB/v1/atm/cycles" -d '{}' | jq// A minimal ATM/ITM driver. The shape a real integration takes: one function that
// posts a machine message and reads back the ISO 8583 projection of the 0200/0210 pair.
const BASE = process.env.FINLAB ?? 'https://ecosystem.financial';
const headers = { 'x-lab-api-key': process.env.FINLAB_API_KEY, 'content-type': 'application/json' };
async function machine(path, message) {
const res = await fetch(BASE + path, { method: 'POST', headers, body: JSON.stringify(message) });
const body = await res.json();
// 422 is a structural refusal - a malformed message, an unknown terminal. A DECLINE is
// not one of those: it arrives 200 with approved:false and a DE039 response code.
if (res.status === 422) throw new Error(body.error + ': ' + body.message);
return body;
}
const [terminal] = (await (await fetch(BASE + '/v1/atm/terminals', { headers })).json()).result;
const withdrawal = await machine('/v1/atm/withdrawals', {
terminalId: terminal.terminalId,
labPan: process.env.LAB_PAN,
pin: process.env.LAB_PIN,
amountMinor: 200_00,
currency: 'DOP',
});
if (withdrawal.result.approved) {
console.log('dispense', withdrawal.result.amountMinor, 'authorization', withdrawal.result.authorizationCode);
} else {
// DE039: 51 insufficient funds, 55 wrong PIN, 75 attempt limit reached, and so on
console.log('decline', withdrawal.result.responseCode, withdrawal.result.reason);
}
// every rule the transaction touched, with the article behind it
for (const finding of withdrawal.findings) console.log(finding.status, finding.ruleId, finding.article);import json, os, urllib.request
BASE = os.environ.get("FINLAB", "https://ecosystem.financial")
H = {"x-lab-api-key": os.environ["FINLAB_API_KEY"], "content-type": "application/json"}
def machine(path, message):
req = urllib.request.Request(BASE + path, data=json.dumps(message).encode(), headers=H, method="POST")
with urllib.request.urlopen(req) as res:
return json.load(res)
withdrawal = machine("/v1/atm/withdrawals", {
"terminalId": "TERM-LAB-0001",
"labPan": os.environ["LAB_PAN"],
"pin": os.environ["LAB_PIN"],
"amountMinor": 200_00,
"currency": "DOP",
})
result = withdrawal["result"]
if result["approved"]:
print("dispense", result["amountMinor"], "authorization", result["authorizationCode"])
else:
# a decline is a response: 200, approved false, and the ISO 8583 DE039 code
print("decline", result["responseCode"])
for finding in withdrawal["findings"]:
print(finding["status"], finding["ruleId"], finding["article"])6La Población del Lab
Every team world is born holding the same documented starter dataset, so a developer with a fresh key does not have to invent an ecosystem before making a real call.
The institutions
| Actor | Lab name | What it is |
|---|---|---|
banco-norte | Banco Norte del Lab | strict-legacy - branch-first, conservative at the door, batch-shaped posting habits |
banco-del-sur | Banco del Sur del Lab | permissive-digital - app-first, onboards remotely in seconds, leans on monitoring rather than on the door |
coop-del-lab | Cooperativa del Lab | cooperative - member-owned, accepts a thin file because membership is the relationship |
epe-lab | EPE del Lab | Electronic-payment entity: electronic payment accounts, three agents, the Art. 78 numeral II caps |
ddc-administrator | Administrador DD/DC del Lab | Direct-debit and direct-credit administrator: batches, cycles on a business calendar, returns, net settlement |
atm-network | Red de Cajeros del Lab | ATM-network administrator: four machines, one of them deliberately out of cash |
central-bank | Banco Central del Lab | LBTR, SGPI, Pagos al Instante, the participants' current accounts, the account standard |
The people, and what they hold
| What | How many |
|---|---|
| synthetic people | 200 |
| businesses | 40 |
| people onboarded at a bank, round-robin across the three | 90 |
| of those, holding a debit card | 30 |
| electronic payment accounts at the EPE | 30 |
| aliases registered in the simulated SGPI directory | 45 |
| businesses with an account | 20 |
| people the Lab has flagged as adversaries | 12 |
The counts are ceilings, not guarantees, and deliberately so: the seeder goes through each institution's own front door, calling the same onboarding a team calls. Banco Norte is a strict institution and it is supposed to refuse people. A starter dataset that quietly bypassed its own door would be teaching the wrong thing in the first minute of the first day.
Opening balances
| Account | Opening balance |
|---|---|
| each bank's own current account at the simulated Central Bank | RD$50,000,000.00 |
| the EPE's float, which must cover every managed balance | RD$5,000,000.00 |
| a personal sight account | RD$25,000.00 |
| a business account | RD$400,000.00 |
| an electronic payment account | RD$8,000.00 |
Why no identifier here can be mistaken for a real one
| Identifier | Scheme, and why it cannot be real |
|---|---|
person | LAB-###-#######-# - the 3-7-1 shape of a cedula de identidad y electoral, so field widths, input masks and validators exercise correctly, behind a LAB- prefix a real cedula can never carry, because a real cedula is eleven digits and separators and never a letter. The prefix is what makes it unmistakably synthetic; the Lab makes no claim about which digit blocks have or have not been issued. |
business | LAB-RNC-######### - the nine digits of a Registro Nacional de Contribuyente behind the same LAB- prefix. |
account | LB + two check digits + a four-character institution code + sixteen digits, the shape of the regional standardized account number with the country prefix deliberately LB and never DO (assumption A-02). |
card | A PAN whose issuer identification number begins with 9 - the range ISO/IEC 7812 reserves for national assignment - so it can never collide with a card-scheme BIN. The check digit is a real Luhn digit, so a validator behaves correctly. |
phone | +1-809-555-#### - the 555 exchange, which is not routed to subscribers. NANP formally reserves only 555-0100 to 555-0199 for fictional use; the Lab uses the whole exchange because two hundred people do not fit in a hundred numbers, and that widening is a Lab decision (assumption A-24). |
email | name@lab.invalid - .invalid is reserved by RFC 2606 and can never be delegated. |
alias | @name#### - the SGPI alias, unique inside one world and meaningless outside it. |
The same block travels with every GET /v1/directory response, so nobody has to come back here
before deciding whether a number is safe to put in a slide.
The adversaries
Twelve of the two hundred are flagged: two mule rings of a controller and three mules each, and four card testers. They are drawn from the end of the population so nobody a scenario needed is quietly turned into a mule, and their onboarding-facing traits are forced clean - because a recruited mule is recruited for exactly that: papers that pass. The pattern has to be found in the movements, never at the door.
The Lab's knowledge of who they are is the oracle a run scores a detection against. Nothing that performs detection may read it, and the monitoring unit reads it only after its alerts already exist.
Resetting your world
POST /v1/worlds/reset throws your team's world away and stands a fresh one up from the same seed.
The people come back with the same documents and the same accounts; everything they did does not. Your
run reports survive a reset, because a report is the record and a reset is not a way to unsay one.
7Scenarios
A scenario is a whole process, written down: a cast, a clock, a population, a sequence of steps with their expectations, and a declaration of the rules it claims to exercise.
Every scenario declares what it cites, and the report lists anything cited but not reached. A scenario that claims a rule and never touches it is a failing scenario. That is not a nicety - it is what stops a catalogue of rules from becoming a catalogue of intentions.
A variant runs the same scenario down a different course: the applicant who is a minor, the machine
that runs out of notes, the order that arrives after the window closed, the institution misconfigured to charge
the beneficiary. Start one with { "scenario": "...", "variant": "..." }, and pass your own
seed to make the run reproducible by anybody you hand the seed to.
adversarial-detectionA mule ring, a card-testing run, and a monitoring unit that is scored on both
The first adversarial run. The synthetic population is asked for a mule ring - three accounts that collect from elsewhere and pass almost everything straight on to a controller - and for a card tester, who tries a card at a machine again and again with a PIN they do not have. The institutions behave normally throughout: nobody is refused for being an adversary, because nothing in the ecosystem knows who is one. Then the simulated monitoring unit consumes what Arts. 43-44 make available - the institutions' own movement records and the network's machine transactions, refusals included - looks for two patterns, and raises alerts. Only afterwards does it read the Lab's own ground truth, which is the one place that knows who was made an adversary, and score itself against it. Neither pattern nor any threshold in it comes from the Reglamento. Art. 43 makes the Ley 155-17 obligation apply and Art. 44 puts the records at the supervisors' disposal; what to look for in them is the Lab's decision, recorded as assumption A-23. A score is a score on this run, this seed and these thresholds. The banks run at the BEHAVIOURAL fidelity tier: the legacy core under a busy-branch-day profile, with jitter, a queue and a long tail; the digital bank on a cautious fraud desk that refuses one machine withdrawal in twenty on its own suspicion, whatever the balance says. The patterns have to be found through that, not in spite of it.
Process: 6.14 Fraud and adversarial - mule networks and card testing in the synthetic population, with a run that scores the detection · Seed: finlab-adversarial-2026
Variants
no-pattern-nothing-to-find- The same institutions, the same monitoring, and nobody doing anything. The other half of a detection claim, and the half that is usually missing: a run with no pattern in it must raise no alert. A monitoring unit that finds a mule ring in an ordinary day is not a detection, it is a noise generator.
Rules it claims to exercise
AML.TRANSACTION_MONITORING ATM.CARDHOLDER_VERIFICATION RECORDS.INCLUDES_REJECTED RECORDS.RETENTION_YEARS TRANSFER.NO_CHARGE_TO_BENEFICIARY ACCOUNT.STANDARD_NUMBER SIPARD.INTEROPERABILITY
atm-cardholder-withdrawalCardholder withdrawal and deposit at a machine of another institution
A customer of the permissive digital bank takes cash out of a machine that the strict legacy bank operates, and then puts notes back into it. The card is one the issuer issued, the PIN is verified at the issuer, and the switch between the machine and the host is the simulated ATM-network administrator at stateful fidelity. Neither institution moves money to the other at the moment of the withdrawal: the position between them joins the network's cycle, and the cycle's net result settles in the simulated LBTR, which is what Art. 79 requires of every clearing cycle of the electronic-instrument systems. The variants are the two things a machine on the Lab floor will do most often by accident: a wrong PIN until the card blocks, and a machine that has run out of notes.
Process: 6.3 Cards / 6.4 ATM-ITM - withdrawal at a machine through the simulated network administrator, deposit with recognition, and settlement of the network's net result in the LBTR (Art. 79) · Seed: finlab-atm-2026
Variants
wrong-pin-blocks-the-card- Three wrong PINs and the card is blocked. A machine on a Lab floor sees a wrong PIN more often than anything else. The issuer counts the attempts, blocks the card on the third, and every refusal is retained in the ten-year record with its response code (Art. 41).machine-out-of-notes- The machine has no notes left. The switch refuses before it ever troubles the issuer, and says so with DE039 51.
Rules it claims to exercise
ATM.CARDHOLDER_VERIFICATION ATM.DEPOSIT_DISCREPANCY_RECORDED NET.SETTLES_IN_LBTR NET.DEFERRED_FINALITY LBTR.SETTLEMENT_CURRENCY SIPARD.INTEROPERABILITY SIPARD.DIRECT_PARTICIPANT_HAS_CB_ACCOUNT CAPITAL.EPE_SUBACQUIRER_ATM_WALLET ACCOUNT.STANDARD_NUMBER RECORDS.RETENTION_YEARS RECORDS.INCLUDES_REJECTED
atm-cardless-withdrawalCardless withdrawal started on the phone and finished at the machine
The phygital flow the Lab floor is built for. A customer opens the bank's app, asks for cash without a card, and the simulated ATM-network administrator mints a one-time code and the QR that carries the data needed to initiate the order - which is what Art. 78 numeral IV requires every provider to be able to present and read. Minutes later the customer walks up to a machine of another institution, keys the code, and takes the notes. Nothing authenticates at the machine: the order was authenticated on the phone, and the order is spent exactly once. The second half is the other thing these machines are for in this country: a remittance that arrived at an electronic-payment entity is paid out in cash at the machine, in full, because the beneficiary of a payment is never charged for receiving it (Art. 81).
Process: 6.4 ATM-ITM phygital - cardless withdrawal started on the phone and finished on the machine, and a remittance paid out in cash at the machine · Seed: finlab-atm-cardless-2026
Variants
order-redeemed-twice- The same code presented at the machine a second time. An order is spent once. A replay is refused, and the rule that refuses it says so by name.order-expired-before-the-machine- The holder took too long to reach the machine. The order is minted with a five-minute validity and the holder arrives after six. The machine refuses, and the account is never touched.
Rules it claims to exercise
ATM.CARDLESS_ORDER_SINGLE_USE ATM.CARDHOLDER_VERIFICATION QR.PRESENT_AND_READ TRANSFER.NO_CHARGE_TO_BENEFICIARY EPA.PERMITTED_OPERATION EPA.BALANCE_CAP NET.SETTLES_IN_LBTR SIPARD.INTEROPERABILITY ACCOUNT.STANDARD_NUMBER
charge-to-beneficiary-must-failA bank that charges the beneficiary must fail
A misconfigured institution is put on the bus on purpose. Its personality carries chargesBeneficiary, so it builds its instructions with an ISO 20022 charge bearer of CRED - the charge borne by the creditor. The Reglamento does not allow it: Art. 81 and Art. 56 numeral II say the beneficiary of a transfer or payment is never charged. The run proves that the rule bites on the institution's own internal transfer, and again at the simulated Central Bank when the instruction reaches the SGPI. This scenario is expected to raise failed findings: that is the point of it.
Process: 6.2 Electronic funds transfers - variant: beneficiary charged (must fail, Art. 81) · Seed: finlab-art81-2026
Variants
No variants.
Rules it claims to exercise
TRANSFER.NO_CHARGE_TO_BENEFICIARY SGPI.MANDATORY_PARTICIPATION RECORDS.INCLUDES_REJECTED
direct-credit-with-returnPayroll by direct credit, with one item returned
An employer hands a payroll file to the simulated direct-debit and direct-credit administrator. The originator's account is debited when the file is accepted. At the clearing cycle each item is applied at the receiving institution; one of them names an account that does not exist, so it comes back with a reason code and the originator is made whole. The net position of the cycle then settles in the simulated LBTR, which is what Art. 79 requires of the results of a clearing cycle.
Process: 6.2 Electronic funds transfers - direct credit through the simulated administrator with returns; 6.12 Business - payroll · Seed: finlab-ddc-2026
Variants
weekend-cycle- A clearing cycle attempted on a Sunday
Rules it claims to exercise
DDC.CYCLE_ON_BUSINESS_DAY DDC.RETURN_CARRIES_REASON_CODE NET.SETTLES_IN_LBTR NET.DEFERRED_FINALITY SIPARD.INTEROPERABILITY RECORDS.INCLUDES_REJECTED
internal-transferInternal transfer between two accounts at one bank
The simplest movement in the ecosystem, and the one every integration starts with: two customers of the same institution, one transfer, no system in between. The run checks that the beneficiary is not charged (Art. 81), that the ordering party bears the fixed fee, that the bank's book balances, and that the movement is in the ten-year record. The variant asks for more than the account holds.
Process: 6.2 Electronic funds transfers - internal · Seed: finlab-internal-2026
Variants
insufficient-funds- The ordering account does not hold the amount
Rules it claims to exercise
TRANSFER.NO_CHARGE_TO_BENEFICIARY ACCOUNT.STANDARD_NUMBER RECORDS.RETENTION_YEARS RECORDS.INCLUDES_REJECTED
lbtr-foreign-currencyHigh-value settlement in dollars and in euros
The LBTR settles continuously in Dominican pesos, US dollars and euros, and Art. 56 puts national and foreign currency on the same footing. Until now every scenario moved pesos, and the currency rule was checked against a single member of its own list. This one moves a hundred thousand dollars between two participants that hold dollar current accounts at the simulated Central Bank, credits the beneficiary within the four minutes Art. 57 allows, and does the same in euros in its variant. Each currency has its own current account and its own queue: a participant with pesos to spare and no dollars cannot settle a dollar order, which is the point of the third variant.
Process: 6.2 Electronic funds transfers - high-value through the LBTR in foreign currency (Art. 56: national and foreign currency) · Seed: finlab-fx-lbtr-2026
Variants
euros- The same settlement in euros. The third of the LBTR's currencies, exercised the same way. Nothing in the flow changes but the current account the order is drawn on, which is the property worth proving.no-liquidity-in-that-currency- Pesos in the vault, no dollars in the account. Each currency has its own current account at the simulated Central Bank and its own queue. A participant seeded only in pesos cannot settle a dollar order: the order queues, and it queues in dollars.
Rules it claims to exercise
LBTR.SETTLEMENT_CURRENCY LBTR.THIRD_PARTY_CREDIT LBTR.IRREVOCABLE_ONCE_SETTLED SIPARD.DIRECT_PARTICIPANT_HAS_CB_ACCOUNT ACCOUNT.STANDARD_NUMBER RECORDS.RETENTION_YEARS
lbtr-high-valueHigh-value transfer through the LBTR, with a queue and a revocation
A high-value order arrives at the simulated LBTR while the ordering bank's current account at the Central Bank does not cover it, so the order queues. Queued, it is revocable, and the run revokes it and watches the debit come back. The Central Bank then credits the bank's current account, a second order settles gross, the receiving bank posts the third-party credit inside the four minutes of Art. 57, and a revocation of the settled order is refused: once settled, an order is irrevocable (Art. 66).
Process: 6.2 Electronic funds transfers - high value through the LBTR with the four-minute third-party credit and irrevocability once settled · Seed: finlab-lbtr-2026
Variants
No variants.
Rules it claims to exercise
LBTR.SETTLEMENT_CURRENCY LBTR.REVOCABLE_WHILE_QUEUED LBTR.IRREVOCABLE_ONCE_SETTLED LBTR.THIRD_PARTY_CREDIT SIPARD.DIRECT_PARTICIPANT_HAS_CB_ACCOUNT
onboarding-account-openingOnboarding and account opening at two banks with different appetites
The same applicant walks into the strict legacy bank and the permissive digital bank. Identity is verified against the simulated civil registry with document and, where the institution requires it, liveness; the bureau is queried; the institution's own policy decides. The run then opens an electronic payment account and funds it, and the RD$79,000 cap of Art. 78 numeral II is enforced on the way in. The variants are the cases a real onboarding desk actually meets: a politically exposed person, a minor, a thin file, a poor document capture, and a funding that would breach the cap.
Process: 6.1 Onboarding and account opening - identity, bureau, risk scoring, account or e-payment account creation (Art. 19), first funding · Seed: finlab-onboarding-2026
Variants
pep-hit- The applicant is a politically exposed personminor-applicant- The applicant is a minorthin-file- A thin credit file, refused by one bank and accepted by the otherdocument-mismatch- The document capture is too poor to verifycap-reached- Funding that would breach the RD$79,000 cap
Rules it claims to exercise
ACCOUNT.STANDARD_NUMBER AML.SCREENING_REQUIRED EPA.NO_INTEREST EPA.BALANCE_CAP EPA.FUNDING_CAP_30D EPA.PERMITTED_OPERATION EPA.ADDITIONAL_CREDENTIALS RECORDS.RETENTION_YEARS RECORDS.INCLUDES_REJECTED AGENT.PERMITTED_ACTIVITY SIPARD.DIRECT_PARTICIPANT_HAS_CB_ACCOUNT
pagos-al-instante-dollarsA dollar transfer through Pagos al Instante, converted by the originating bank
Two things the Central Bank says about Pagos al Instante that no scenario had exercised yet: a transfer in dollars needs no correspondent bank, and where the beneficiary's account is held in another currency the originating institution may convert, so that the beneficiary is credited in the currency of their own account. A customer of the strict legacy bank holds dollars and pays a customer of the permissive digital bank who holds pesos. The originating bank converts, records the conversion with the transaction as Arts. 41-42 require of the ten-year record, and instructs a peso order that settles in the LBTR - which settles in national and foreign currency alike (Art. 56), which is exactly why no correspondent is needed. The rates are Lab indicative figures, deliberately round and deliberately fictitious (assumption A-21). Nothing in this repository uses a published rate.
Process: 6.2 Electronic funds transfers - Pagos al Instante BCRD in dollars: no correspondent, and conversion by the originating institution when the beneficiary account is in another currency · Seed: finlab-pai-fx-2026
Variants
dollars-to-dollars- Dollars into a dollar account, with nothing to convert. The other half of the same statement. When the beneficiary already holds dollars there is nothing to convert, and still no correspondent: the order settles gross in the LBTR's dollar leg.correspondent-must-fail- A bank that insists on a correspondent for dollars. The misconfiguration switch, in the shape the repository already uses for Art. 81: a bank configured to route dollar transfers through a correspondent is refused, the rule fails by name, and the refusal is retained in the record.
Rules it claims to exercise
PAI.USD_NO_CORRESPONDENT PAI.ORIGINATOR_CONVERTS FX.CONVERSION_RECORDED PAI.FINAL_CREDIT PAI.OPERATING_WINDOW PAI.FIXED_FEE_ONLY PAI.NO_DEDUCTION_BY_RECEIVING_BANK LBTR.SETTLEMENT_CURRENCY TRANSFER.NO_CHARGE_TO_BENEFICIARY RECORDS.RETENTION_YEARS RECORDS.INCLUDES_REJECTED
pagos-al-instantePagos al Instante BCRD - eight minutes, and the window that closes at four
Pagos al Instante BCRD runs on the LBTR and credits the beneficiary finally within eight minutes. It is available seven days a week from 07:00 to 23:00 local time, with a pause on weekdays between 16:00 and 18:30, and an order given outside that schedule becomes effective at 08:00 on the NEXT BUSINESS DAY - «se hacen efectivos a las 8:00 a.m. del siguiente dia laborable», BCRD Pagos al Instante page. The base run sends one transfer inside the window on a Tuesday morning and measures the credit. The variants send one into the weekday pause, one after 23:00, and one on a Friday night that has to wait the whole weekend - and watch the simulated Central Bank hold each of them, raising a finding on the window rule as it does, because the instruction did arrive outside it.
Process: 6.2 Electronic funds transfers - Pagos al Instante BCRD with the eight-minute credit, the 07:00-23:00 window, and the deferral to 08:00 on the next business day · Seed: finlab-pai-2026
Variants
weekday-pause- An order given during the weekday pause waits for the next business morningafter-hours- An order given after 23:00 becomes effective at 08:00 the next business dayfriday-night-waits-for-monday- A Friday-night order waits the whole weekend, because the page says business day. The correction that made this variant worth writing. A generic "next instant the window opens" would have executed this on Saturday at 07:00; the BCRD page says «el siguiente dia laborable», so it executes on MONDAY at 08:00. Friday 13 March 2026 at 23:30 local becomes Monday 16 March at 08:00 local, which is 12:00Z.
Rules it claims to exercise
PAI.FINAL_CREDIT PAI.OPERATING_WINDOW PAI.FIXED_FEE_ONLY PAI.NO_DEDUCTION_BY_RECEIVING_BANK LBTR.THIRD_PARTY_CREDIT TRANSFER.NO_CHARGE_TO_BENEFICIARY
sgpi-instant-aliasSGPI instant payment addressed by alias
A customer of the strict legacy bank pays a customer of the permissive digital bank through the simulated Sistema de Gestion de Pagos Instantaneos, addressing the beneficiary by alias. The run checks that the payment is final inside ten seconds, that both parties are notified, that the alias resolved through the Central Bank's directory, and that both institutions are registered participants as Art. 61 numeral I requires. It is run at 03:00 on a Sunday on purpose: the SGPI has no operating window.
Process: 6.2 Electronic funds transfers - SGPI with the ten-second credit, alias resolution and 24/7/365 · Seed: finlab-sgpi-2026
Variants
unregistered-beneficiary-alias- The alias is not in the directory. An instant payment addressed to an alias nobody registered must fail cleanly.
Rules it claims to exercise
SGPI.FINAL_CREDIT SGPI.AVAILABILITY SGPI.MANDATORY_PARTICIPATION SGPI.NOTIFY_BOTH_PARTIES SGPI.ALIAS_ADDRESSING SIPARD.SGPI_MANAGED_BY_CENTRAL_BANK SIPARD.DIRECT_PARTICIPANT_HAS_CB_ACCOUNT TRANSFER.NO_CHARGE_TO_BENEFICIARY ACCOUNT.STANDARD_NUMBER
Running a whole scenario
# A whole process, end to end, from one call: onboarding at a strict institution,
# with the five variants that make it interesting.
export FINLAB="https://ecosystem.financial"
# what is on offer, and what each one claims to exercise
curl -s -H "x-lab-api-key: $FINLAB_API_KEY" "$FINLAB/v1/scenarios" | jq '.[] | {id, title, variants: [.variants[].id]}'
# the base run
curl -s -X POST "$FINLAB/v1/runs" \
-H "x-lab-api-key: $FINLAB_API_KEY" -H 'content-type: application/json' \
-d '{ "scenario": "onboarding-account-opening" }' | jq '{id, passed, summary}'
# the same scenario with the applicant who breaches the funding cap, on your own seed,
# so the run is reproducible by anybody you hand the seed to
curl -s -X POST "$FINLAB/v1/runs" \
-H "x-lab-api-key: $FINLAB_API_KEY" -H 'content-type: application/json' \
-d '{ "scenario": "onboarding-account-opening", "variant": "cap-reached", "seed": "our-team-2026-09-08" }' \
| jq '{id, passed}'
# the report, in JSON and rendered as Markdown
RUN=$(curl -s -H "x-lab-api-key: $FINLAB_API_KEY" "$FINLAB/v1/runs" | jq -r '.[0].id')
curl -s -H "x-lab-api-key: $FINLAB_API_KEY" "$FINLAB/v1/runs/$RUN" | jq '.findings[] | select(.status=="fail")'
curl -s -H "x-lab-api-key: $FINLAB_API_KEY" "$FINLAB/v1/runs/$RUN/report.md" > report.mdconst BASE = process.env.FINLAB ?? 'https://ecosystem.financial';
const headers = { 'x-lab-api-key': process.env.FINLAB_API_KEY, 'content-type': 'application/json' };
const call = async (path, init) => (await fetch(BASE + path, { headers, ...init })).json();
// run every variant of one scenario and see which rules each of them reached
const [scenario] = (await call('/v1/scenarios')).filter((s) => s.id === 'onboarding-account-opening');
const variants = ['base', ...scenario.variants.map((v) => v.id)];
for (const variant of variants) {
const started = await call('/v1/runs', {
method: 'POST',
body: JSON.stringify({ scenario: scenario.id, variant, seed: 'our-team-2026-09-08' }),
});
const report = await call('/v1/runs/' + encodeURIComponent(started.id));
const failed = report.findings.filter((f) => f.status === 'fail');
console.log(
variant.padEnd(22),
started.passed ? 'PASS' : 'FAIL',
report.rulesExercised.length + ' rules',
failed.map((f) => f.ruleId + ' (' + f.article + ')').join(', '),
);
}import json, os, urllib.request
BASE = os.environ.get("FINLAB", "https://ecosystem.financial")
H = {"x-lab-api-key": os.environ["FINLAB_API_KEY"], "content-type": "application/json"}
def call(path, body=None):
data = json.dumps(body).encode() if body is not None else None
req = urllib.request.Request(BASE + path, data=data, headers=H, method="POST" if data else "GET")
with urllib.request.urlopen(req) as res:
return json.load(res)
scenario = next(s for s in call("/v1/scenarios") if s["id"] == "onboarding-account-opening")
for variant in ["base"] + [v["id"] for v in scenario["variants"]]:
started = call("/v1/runs", {"scenario": scenario["id"], "variant": variant, "seed": "our-team-2026-09-08"})
report = call("/v1/runs/" + started["id"])
failed = [f for f in report["findings"] if f["status"] == "fail"]
print(
variant.ljust(22),
"PASS" if started["passed"] else "FAIL",
f"{len(report['rulesExercised'])} rules",
", ".join(f"{f['ruleId']} ({f['article']})" for f in failed),
)8Rule catalogue
76 rules, catalogue version 2026.09.05. Every one of them cites
the article it executes, the instrument the article belongs to, the resolution that fixed its value and the date
of that resolution. A rule that does not carry all four fails to load - this is enforced in the
loader, not merely asked for.
Two rules carry no article of the Reglamento at all, and say so in the article field itself: one
Lab control and one governance rule. Several more state in their source exactly which half is the
Reglamento's and which half is ours. A threshold with an article beside it that the article does not
carry is a fabricated regulatory fact, and this catalogue is built so that one cannot be written by
accident.
When a value changes - the Junta Monetaria adjusts a cap each January, and the high-value threshold is CPI-adjusted each year - it changes in one place, and the catalogue version changes with it.
Account numbering 1
ACCOUNT.STANDARD_NUMBERNovena Resolucion, 18 November 2010major
Accounts are numbered to the regional standardized account number adopted in 2010. The Lab uses an account number of the same shape with an LB prefix and ISO 7064 mod 97-10 check digits so it can never be mistaken for a real account.
- Article
Novena Resolucion, 18 November 2010- Instrument
- Novena Resolucion de la Junta Monetaria
- Resolution
- Novena Resolucion de la Junta Monetaria
- Date
2010-11-18- Check
must_be_true- Parameter
account_number_valid- Value
true- Source
- Novena Resolucion, 18 November 2010; field layout is Lab assumption A-02
Agents 1
AGENT.PERMITTED_ACTIVITYArt. 26-27major
Enrol users, fund accounts, pay out cash, show balances and deliver cards.
- Article
Art. 26-27- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_member- Parameter
agent_activity- Value
["enrol","fund","cash-out","balance","card-delivery"]- Source
- Reglamento Arts. 26-27
Prevention of money laundering 2
AML.SCREENING_REQUIREDArt. 43-44major
Obligations under Ley 155-17 apply to payment-service providers and participants; records must be available to supervisors.
- Article
Art. 43-44- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
screening_performed- Value
true- Source
- Reglamento Art. 43-44; Ley 155-17
AML.TRANSACTION_MONITORINGArt. 43-44critical
Anti-money-laundering obligations under Ley 155-17 apply to payment-service providers and participants, and their records must be available to the supervisors. A run that claims monitoring must show what was reviewed: the movements of the institutions and the machine transactions of the networks, refusals included.
- Article
Art. 43-44- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
monitoring_reviewed_the_movements- Value
true- Source
- Reglamento Arts. 43-44 (Ley 155-17 obligations, and records at the supervisors' disposal). The article requires the obligation and the availability of the records; it prescribes no detection pattern and no threshold. The patterns the Lab looks for, and every number in them, are Lab decisions recorded as assumption A-23.
Machines 3
ATM.CARDHOLDER_VERIFICATIONLab controlcritical
No withdrawal at a machine is authorized until the issuer has verified the holder: a PIN on the card, a biometric check through the identity entity, or an order the holder already authenticated on the phone. This is a Lab control, not an article of the Reglamento; the Reglamento's own requirement on this flow is that every transaction, including the refused ones, is recorded (Art. 41).
- Article
Lab control- Instrument
- CEMI Financial Innovation Lab governance
- Resolution
- Plan approved by Carlos Miranda Levy
- Date
2026-09-04- Check
must_be_true- Parameter
cardholder_verified- Value
true- Source
- Lab control, not a regulatory statement. The number of PIN attempts before a card is blocked is assumption A-19.
- Notes
- Catalogued so a run reports it next to the articles it does execute. Nothing in the sources read for the plan states a cardholder-verification obligation for ATM transactions; if the instructivo for administrators of payment systems does, this rule is rewritten with its citation.
ATM.CARDLESS_ORDER_SINGLE_USEArt. 78 SIVcritical
An order started on the phone and finished at the machine may be redeemed exactly once and only inside its validity. Art. 78 numeral IV requires the QR that carries it to hold the data needed to initiate a payment order; that the order is single-use and time-limited is a Lab control.
- Article
Art. 78 SIV- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
order_redeemable_once_within_validity- Value
true- Source
- Reglamento Art. 78 numeral IV for the QR; the single-use and time-limit controls are Lab decisions (assumption A-20).
ATM.DEPOSIT_DISCREPANCY_RECORDEDArt. 41-42major
A deposit at a machine credits what the machine recognized. Where that differs from what was declared, the difference is recorded as an adjustment, because the record of every transaction must include adjustments and be retained for ten years.
- Article
Art. 41-42- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
recognition_discrepancy_recorded- Value
true- Source
- Reglamento Arts. 41-42; the recognition model itself is assumption A-20.
Minimum capital 3
CAPITAL.SYSTEM_ADMINISTRATOR_AND_ACQUIRERTitulos II-Vinformational
RD$78,866,000 minimum paid-in capital, the 2026 value.
- Article
Titulos II-V- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Quinta Resolucion de la Junta Monetaria
- Date
2026-01-29- Check
manual- Parameter
minimum_paid_in_capital- Value
78866000 DOP- Source
- Quinta Resolucion of 29 January 2026, adjusting the Reglamento's values by CPI
CAPITAL.EPE_SUBACQUIRER_ATM_WALLETTitulos II-Vinformational
RD$19,720,000 minimum paid-in capital, the 2026 value.
- Article
Titulos II-V- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Quinta Resolucion de la Junta Monetaria
- Date
2026-01-29- Check
manual- Parameter
minimum_paid_in_capital- Value
19720000 DOP- Source
- Quinta Resolucion of 29 January 2026
CAPITAL.INITIATION_PROVIDERTitulos II-Vinformational
RD$9,445,500 minimum paid-in capital, the 2026 value.
- Article
Titulos II-V- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Quinta Resolucion de la Junta Monetaria
- Date
2026-01-29- Check
manual- Parameter
minimum_paid_in_capital- Value
9445500 DOP- Source
- Quinta Resolucion of 29 January 2026
Cards 1
CARD.PREPAID_CAPArt. 78 SIIcritical
The same RD$79,000 cap applies to prepaid cards.
- Article
Art. 78 SII- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Quinta Resolucion de la Junta Monetaria
- Date
2026-01-29- Check
max_amount- Parameter
prepaid_cap- Value
79000 DOP- Source
- Reglamento Art. 78 numeral II
Claims 1
CLAIM.TYPIFICATIONIN-36-023 numeral 10 f) ivmajor Pending re-check: 2025 Reglamento
The normas de funcionamiento must provide an auditable register for the control of claims and their answers, typified as attributable to operational or technological errors of the system administrator, its participants, the acquirer or its affiliated establishments; or derived from claims made by clients to participants or to users of the payment instruments processed.
- Article
IN-36-023 numeral 10 f) iv- Instrument
- Instructivo para los Administradores de Sistemas de Pago o de Liquidacion de Valores
- Resolution
- IN-36-023 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
must_be_member- Parameter
claim_class- Value
["operational-or-technological","client-originated"]- Source
- IN-36-023 p.9 numeral 10 f) iv. These are the same two classes that carry the two response deadlines of IN-36-005 numeral 36, which is why the typification lives beside them here.
- Notes
- IN-36-023 is version 01 of 30 July 2021 and cross-references the SUPERSEDED Reglamento of 29 January 2021 throughout. The two-class typification is unlikely to have moved, but the article numbering it sits inside has, so the rule is flagged for re-check.
Direct debit and direct credit 4
DDC.RETURN_CARRIES_REASON_CODEArt. 41; Art. 79major
An item the receiving institution cannot apply is returned to the administrator with a reason code, and the return is part of the ten-year record.
- Article
Art. 41; Art. 79- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
return_has_reason_code- Value
true- Source
- Reglamento Art. 41 on records; Art. 79 on settlement of cycle results
- Notes
- The reason-code list itself is a Lab convention (assumption A-04) until the instructivo for administrators of payment systems is read in full.
DDC.CYCLE_ON_BUSINESS_DAYArt. 79major
Clearing cycles of the direct debit and direct credit systems run on business days.
- Article
Art. 79- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
cycle_ran_on_business_day- Value
true- Source
- Reglamento Art. 79; the calendar itself is Lab configuration (assumption A-01)
DDC.CREDITING_TIME_IS_DECLARED_NOT_FIXEDIN-36-023 numeral 10 c)critical Pending re-check: 2025 Reglamento
The normas de funcionamiento of every payment system must state the moment at which transfers of funds or securities are accepted by the system and become irrevocable, and the time to credit those funds to the final beneficiary of payment orders after acceptance. The Central Bank fixes no universal figure; the obligation is to declare one.
- Article
IN-36-023 numeral 10 c)- Instrument
- Instructivo para los Administradores de Sistemas de Pago o de Liquidacion de Valores
- Resolution
- IN-36-023 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
must_be_true- Parameter
system_declares_crediting_time_and_irrevocability_instant- Value
true- Source
- IN-36-023 p.9 numeral 10 c). This closes the question the spec asked of this document: the crediting time is DELEGATED to each administrator, so the checkable rule is the obligation to declare it. The simulated administrator declares its own and the run records what it declared, which is exactly the shape assumption A-03 gave the parameter.
DDC.CLAIM_RECEPTION_YEARSIN-36-023 numeral 10 f) iiimajor Pending re-check: 2025 Reglamento
The normas de funcionamiento must provide a claim-reception period of up to four years, counted from the moment the user becomes aware of the event giving rise to the claim.
- Article
IN-36-023 numeral 10 f) iii- Instrument
- Instructivo para los Administradores de Sistemas de Pago o de Liquidacion de Valores
- Resolution
- IN-36-023 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
retention- Parameter
system_claim_reception_years- Value
4 years- Source
- IN-36-023 p.9 numeral 10 f) iii. The anchor - the user's knowledge - differs from the one IN-36-024 numeral 49 gives the same four years for an electronic payment account.
Electronic payment accounts 16
EPA.BALANCE_CAPArt. 78 SIIcritical
The balance of an electronic payment account may not exceed RD$79,000, the 2026 value of the cap adjusted yearly by the Junta Monetaria.
- Article
Art. 78 SII- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Quinta Resolucion de la Junta Monetaria
- Date
2026-01-29- Check
max_amount- Parameter
balance_cap- Value
79000 DOP- Source
- Reglamento Art. 78 numeral II; value set by the Quinta Resolucion of 29 January 2026
- Notes
- Re-check every January when the Junta Monetaria adjusts the Reglamento's values.
EPA.FUNDING_CAP_30DArt. 78 SIIcritical
Funding of an electronic payment account may not exceed RD$79,000 within any 30-day window.
- Article
Art. 78 SII- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Quinta Resolucion de la Junta Monetaria
- Date
2026-01-29- Check
max_amount- Parameter
funding_cap_30_days- Value
79000 DOP- Source
- Reglamento Art. 78 numeral II; value set by the Quinta Resolucion of 29 January 2026
EPA.NO_INTERESTArt. 19major
The balance equals the nominal value received, bears no interest and is refundable on demand.
- Article
Art. 19- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_false- Parameter
interest_accrued- Value
false- Source
- Reglamento Art. 19-25
EPA.PERMITTED_OPERATIONArt. 20major
Cash withdrawal, direct credit, direct debit, POS, ATM withdrawal, e-commerce, airtime and data top-ups, bill payment, inbound remittances and social subsidies.
- Article
Art. 20- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_member- Parameter
operation- Value
["cash-withdrawal","direct-credit","direct-debit","pos","atm-withdrawal","e-commerce","top-up","bill-payment","inbound-remittance","social-subsidy"]- Source
- Reglamento Art. 20
EPA.ADDITIONAL_CREDENTIALSArt. 19 SVminor
An account holder may hold additional credentials on the same balance, each with its own limits.
- Article
Art. 19 SV- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
manual- Parameter
credential_limits_enforced- Value
true- Source
- Reglamento Art. 19 numeral V
EPA.FUNDING_AVAILABLE_AFTER_SETTLEMENTIN-36-024 numeral 23 a)critical Pending re-check: 2025 Reglamento
Availability in the electronic payment account of funding made through other payment instruments carrying deferred crediting must occur at the latest two hours after its settlement, when credited through electronic payment instruments.
- Article
IN-36-024 numeral 23 a)- Instrument
- Instructivo para las Entidades de Pago Electronico y Cuentas de Pago Electronico
- Resolution
- IN-36-024 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
max_duration- Parameter
epa_funding_availability_after_settlement- Value
2 h- Source
- IN-36-024 p.11 numeral 23 a). The reference instant is SETTLEMENT of the funding instrument, not the instruction - the instructivo says "luego de su liquidacion" - which is also the anchor assumption A-11 chose for the Art. 57 four minutes, now supported by analogy rather than only by the Lab's reading.
EPA.FUNDING_AVAILABLE_AFTER_CHEQUEIN-36-024 numeral 23 b)critical Pending re-check: 2025 Reglamento
Availability in the electronic payment account must occur at the latest thirty minutes after the cheque is credited to the bank account of the electronic payment entity providing the service.
- Article
IN-36-024 numeral 23 b)- Instrument
- Instructivo para las Entidades de Pago Electronico y Cuentas de Pago Electronico
- Resolution
- IN-36-024 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
max_duration- Parameter
epa_funding_availability_after_cheque- Value
30 m- Source
- IN-36-024 p.11 numeral 23 b). The reference instant is the cheque landing in the EPE's OWN bank account, which is a different instant from the cheque being deposited. The SCC that would clear the cheque is Phase 3, so the run supplies that instant explicitly.
EPA.CAP_WINDOW_IS_CALENDAR_DAYSIN-36-024 numeral 18 b)critical Pending re-check: 2025 Reglamento
The technological solution must show the holder the headroom remaining against the issuance or funding limit permitted within the 30 calendar-day period in which the account currently sits, and must notify the user when that ceiling is reached.
- Article
IN-36-024 numeral 18 b)- Instrument
- Instructivo para las Entidades de Pago Electronico y Cuentas de Pago Electronico
- Resolution
- IN-36-024 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
must_equal- Parameter
epa_cap_window_calendar_days- Value
30 calendar days- Source
- IN-36-024 p.10 numeral 18 b). This settles the UNIT of assumption A-12 - calendar days, not elapsed simulated hours - and the EPE now counts the window on local dates. It does NOT settle rolling versus fixed anchoring: "el periodo de 30 dias calendario en que se encuentre" reads more naturally as a fixed period, and A-12 stays open on that half.
EPA.FUNDING_OVER_CAP_REJECTEDIN-36-024 numeral 19 d)critical Pending re-check: 2025 Reglamento
The technological solution must have the capacity to reject the funding of accounts when it exceeds the limit established for the user.
- Article
IN-36-024 numeral 19 d)- Instrument
- Instructivo para las Entidades de Pago Electronico y Cuentas de Pago Electronico
- Resolution
- IN-36-024 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
must_be_true- Parameter
epa_over_cap_funding_rejected- Value
true- Source
- IN-36-024 p.11 numeral 19 d). Evaluated where the EPE refuses: the observed value is whether the funding was actually rejected once EPA.BALANCE_CAP or EPA.FUNDING_CAP_30D failed, which turns a cap breach from a finding into a refusal.
EPA.NO_OVERDRAFTIN-36-024 numeral 20critical Pending re-check: 2025 Reglamento
The payment instrument shall not permit overdrafts where funds are insufficient to carry out an operation; in that case the payment order shall be rejected.
- Article
IN-36-024 numeral 20- Instrument
- Instructivo para las Entidades de Pago Electronico y Cuentas de Pago Electronico
- Resolution
- IN-36-024 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
must_be_false- Parameter
epa_overdraft_permitted- Value
false- Source
- IN-36-024 p.11 numeral 20; the same numeral also forbids a minimum balance
EPA.FLOAT_FULLY_BACKED_AT_CENTRAL_BANKIN-36-024 numeral 26 a) y c)critical Pending re-check: 2025 Reglamento
Before managing electronic payment accounts the electronic payment entity must hold in its Central Bank current account the funds corresponding to the account to be managed, provisioned by electronic funds transfer, and must keep account reconciliations so that the funds held for its users correspond to the balance in its Central Bank current account and the resources previously deposited to guarantee the managed balances.
- Article
IN-36-024 numeral 26 a) y c)- Instrument
- Instructivo para las Entidades de Pago Electronico y Cuentas de Pago Electronico
- Resolution
- IN-36-024 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
must_be_true- Parameter
epa_float_covers_managed_balances- Value
true- Source
- IN-36-024 p.12 numeral 26 opening, a) and c). This is a full-reserve invariant the Lab's reconciliation layer can assert directly, and it is stronger than the network-wide mirror-sum-zero check that assumption A-14 describes.
EPA.DAILY_REPORT_TO_CENTRAL_BANKIN-36-024 numeral 26 d)major Pending re-check: 2025 Reglamento
The electronic payment entity must report daily to the Central Bank, through the mechanisms it provides, information on the enablement of electronic payment accounts, the funding and the withdrawals made by its users, and the total balance held in their favour.
- Article
IN-36-024 numeral 26 d)- Instrument
- Instructivo para las Entidades de Pago Electronico y Cuentas de Pago Electronico
- Resolution
- IN-36-024 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
must_be_true- Parameter
epa_daily_report_submitted- Value
true- Source
- IN-36-024 p.12 numeral 26 d). The mechanism the Central Bank provides is not described, so the Lab sends the report over the bus to the simulated Central Bank and reports the four figures the numeral names, and nothing more.
EPA.CLAIM_RECEPTION_YEARSIN-36-024 numeral 49major Pending re-check: 2025 Reglamento
Users of an electronic payment account may bring claims before the electronic payment entity within a period of no more than four years, counted from the moment the event giving rise to the claim occurs.
- Article
IN-36-024 numeral 49- Instrument
- Instructivo para las Entidades de Pago Electronico y Cuentas de Pago Electronico
- Resolution
- IN-36-024 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
retention- Parameter
epa_claim_reception_years- Value
4 years- Source
- IN-36-024 p.19 numeral 49. Note the anchor differs from IN-36-023 numeral 10 f) iii, which runs the same four years from the user's KNOWLEDGE of the event rather than from the event. The two documents are not reconciled and the Lab does not reconcile them.
EPA.CLAIM_RESPONSE_DAYSIN-36-024 numeral 49major Pending re-check: 2025 Reglamento
The entity must have agile and reasonable claim-reception mechanisms and must answer claims within a maximum of thirty calendar days from the date of receipt.
- Article
IN-36-024 numeral 49- Instrument
- Instructivo para las Entidades de Pago Electronico y Cuentas de Pago Electronico
- Resolution
- IN-36-024 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
max_count- Parameter
epa_claim_response_calendar_days- Value
30 calendar days- Source
- IN-36-024 p.19 numeral 49, continuing on p.20
EPA.NO_FEE_FOR_CLAIMSIN-36-024 numeral 47 e) y f)major Pending re-check: 2025 Reglamento
In its relationship with users the electronic payment entity shall not charge commissions or fees for claims made, and shall immediately rectify the situations giving rise to a claim where the outcome so determines.
- Article
IN-36-024 numeral 47 e) y f)- Instrument
- Instructivo para las Entidades de Pago Electronico y Cuentas de Pago Electronico
- Resolution
- IN-36-024 version 01, aprobado por el Gobernador del Banco Central
- Date
2021-07-30- Check
must_equal- Parameter
epa_claim_fee_minor- Value
0 DOP- Source
- IN-36-024 p.19 numeral 47 e) and f)
EPA.FLOAT_ONLY_AT_CENTRAL_BANKArt. 22critical
The funds an electronic payment entity holds against the balances it manages may be kept ONLY in its current account at the Central Bank, or in securities issued by the Central Bank or the Ministerio de Hacienda pledged in favour of the Central Bank. They may not be held in a commercial bank, and they may not be invested in anything else.
- Article
Art. 22- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_member- Parameter
epa_float_placement- Value
["central-bank-current-account","pledged-bcrd-securities","pledged-hacienda-securities"]- Source
- Reglamento Art. 22, verified on 2026-09-05. This is the placement rule and it is stronger than the coverage rule: IN-36-024 numeral 26 requires the float to COVER the managed balances, and Art. 22 says WHERE it may sit. A float fully covering its balances but held at a commercial bank satisfies the first and breaches the second.
- Notes
- The pledged-securities placements are catalogued and unexercised: the ecosystem has no securities settlement system before Phase 3, so the only placement a run can take is the Central Bank current account.
Foreign currency 1
FX.CONVERSION_RECORDEDArt. 41-42major
A conversion performed on a transfer is part of the transaction record: the currencies, the rate applied, the amount debited and the amount credited are retained for ten years with the transaction they belong to, and are available to the account holder and to the supervisors.
- Article
Art. 41-42- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
conversion_recorded- Value
true- Source
- Reglamento Arts. 41-42. The rates themselves are Lab configuration and are deliberately fictitious (assumption A-21); no published rate is used anywhere in this repository.
Payment initiation 1
INITIATION.NEVER_HOLDS_FUNDSArt. 4 mm, 38-40critical
Initiation of payment orders on a user's account happens by verifiable consent through secure interfaces; the provider never holds the funds.
- Article
Art. 4 mm, 38-40- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_false- Parameter
initiator_held_funds- Value
false- Source
- Reglamento Art. 4 mm and Arts. 38-40
Lab governance 2
LAB.NOT_AMBIENTE_DE_PRUEBAArt. 83critical
The simulation involves no real providers and no real external users, so it does not fall under the no-objection regime of Art. 83. A run is never a substitute for the Central Bank's no objection; its report is evidence for a request, nothing more.
- Article
Art. 83- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
run_involves_only_simulated_counterparts- Value
true- Source
- Reglamento Art. 83; plan section 10
LAB.SIMULATOR_CARRIES_LAB_NAMEgovernancecritical
No simulator presents itself as the entity it is modelled after. No real credentials, keys, BINs, routing identifiers, aliases or certificates exist anywhere in a run.
- Article
governance- Instrument
- CEMI Financial Innovation Lab governance
- Resolution
- Plan approved by Carlos Miranda Levy
- Date
2026-09-04- Check
must_be_true- Parameter
simulator_uses_lab_identity- Value
true- Source
- plan section 10
LBTR: settlement, priority, finality 14
LBTR.THIRD_PARTY_CREDITArt. 57critical
A credit in favour of a third party must be posted within four minutes of the receiving participant's account being affected.
- Article
Art. 57- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
max_duration- Parameter
third_party_credit_minutes- Value
4 m- Source
- Reglamento Art. 57
LBTR.IRREVOCABLE_ONCE_SETTLEDArt. 66critical
An order settled in the LBTR is irrevocable; only a queued order may be revoked.
- Article
Art. 66- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_false- Parameter
revocation_accepted_after_settlement- Value
false- Source
- Reglamento Art. 66; BCRD Sistema LBTR page
LBTR.REVOCABLE_WHILE_QUEUEDArt. 66major
While an order sits in the LBTR queue and has not settled, the submitting participant may revoke it.
- Article
Art. 66- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
revocation_accepted_while_queued- Value
true- Source
- Reglamento Art. 66; BCRD Sistema LBTR page
LBTR.SETTLEMENT_CURRENCYBCRD Sistema LBTRmajor
The real-time gross settlement system settles continuously in Dominican pesos, US dollars and euros.
- Article
BCRD Sistema LBTR- Instrument
- BCRD published description of the LBTR
- Resolution
- BCRD Sistema LBTR page
- Date
2026-09-04- Check
must_be_member- Parameter
settlement_currency- Value
["DOP","USD","EUR"]- Source
- BCRD Sistema LBTR page
LBTR.HIGH_VALUE_THRESHOLDIN-36-005 numeral 69critical
Electronic funds transfers exceeding RD$15,800,000.00, or the equivalent in foreign currency, are high-value transfers and must necessarily settle through the Central Bank's LBTR. Lower amounts may go through the LBTR or through any authorized payment system. The figure is adjusted annually by the year-on-year change in the Consumer Price Index published by the Central Bank, so it is re-checked each year exactly as the 2026 Junta Monetaria values are.
- Article
IN-36-005 numeral 69- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
max_amount- Parameter
non_lbtr_transfer_amount- Value
15800000 DOP- Source
- IN-36-005 p.22 numeral 69 and Parrafos I-II. Evaluated on every payment routed on a rail OTHER than the LBTR proper - an SGPI instant payment and a Pagos al Instante order - which is where the obligation bites. CPI-adjusted annually; re-check each year.
- Notes
- The equivalence in foreign currency is stated by the instructivo but the conversion basis is not; a run in USD or EUR therefore does not evaluate this rule and says so.
LBTR.PRIORITY_RANGE_PARTICIPANTIN-36-005 numeral 22 Parrafo Imajor
The LBTR offers participants 80 priorities, 20 being the highest and 99 the lowest. Assigning a priority is optional.
- Article
IN-36-005 numeral 22 Parrafo I- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
within_range- Parameter
participant_priority- Value
[20,99]- Source
- IN-36-005 p.13 numeral 22 Parrafo I
LBTR.PRIORITY_DEFAULTIN-36-005 numeral 22 Parrafos I-IImajor
Where the originating participant assigns no priority, the LBTR assigns priority 98. The same default applies to a securities-settlement delivery-versus-payment instruction whose administrator set none.
- Article
IN-36-005 numeral 22 Parrafos I-II- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
must_equal- Parameter
default_priority- Value
98- Source
- IN-36-005 p.13 numeral 22 Parrafos I y II
LBTR.PRIORITY_RESERVED_TO_CENTRAL_BANKIN-36-005 numeral 22 Parrafo IIImajor
The Central Bank holds, exclusively and permanently, the priority range 0 to 14. No participant instruction may carry a priority inside it.
- Article
IN-36-005 numeral 22 Parrafo III- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
must_be_false- Parameter
participant_instruction_uses_reserved_priority- Value
false- Source
- IN-36-005 p.13 numeral 22 Parrafo III
LBTR.PRIORITY_RANGE_DVPIN-36-005 numeral 22 Parrafo IIminor
Funds-transfer instructions sent by the administrator of a securities settlement system under delivery-versus-payment may be assigned a priority in the range 15 to 98.
- Article
IN-36-005 numeral 22 Parrafo II- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
within_range- Parameter
dvp_priority- Value
[15,98]- Source
- IN-36-005 p.13 numeral 22 Parrafo II. Catalogued and unexercised: no securities settlement system exists in the ecosystem before Phase 3, so nothing sends a DvP instruction yet.
LBTR.SETTLEMENT_PRELATIONIN-36-005 numeral 21major
Settlement precedence, highest to lowest, is: Central Bank operations debiting participant accounts; Sistema Electronico de Subastas operations; SGPI results; cheque clearing (SCC) results; Plataforma Cambiaria BCRD results; results of deferred net settlement systems run by other administrators; and participant funds transfers for own or third-party account.
- Article
IN-36-005 numeral 21- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
must_be_member- Parameter
settlement_prelation- Value
["central-bank-debit","auction-css","sgpi-results","cheque-clearing-scc","plataforma-cambiaria","deferred-net-settlement","participant-transfer"]- Source
- IN-36-005 p.12 numeral 21 a-g. The ORDER of the list is the rule, and the queue in packages/central-bank/src/central-bank.ts drains in exactly this order, then by priority, then by time of submission. Two of the seven classes have no actor in the ecosystem yet - the auction system and the Plataforma Cambiaria - and the SCC arrives in Phase 3.
LBTR.QUEUE_ON_INSUFFICIENT_FUNDSIN-36-005 numerales 16 y 23major
The LBTR settles instructions one by one on their value date provided the originating participant has funds in its Central Bank current account. On an insufficient balance the instruction queues, in state Listo, awaiting sufficient funds.
- Article
IN-36-005 numerales 16 y 23- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
must_be_true- Parameter
insufficient_funds_queues_not_rejects- Value
true- Source
- IN-36-005 p.10 numeral 16, p.13 numeral 23
LBTR.REVOCABLE_UNTIL_SETTLEDIN-36-005 numeral 18major
Funds-transfer instructions may be cancelled by the participant that made them or by the Central Bank, at any time, so long as they have not settled in the LBTR. This refines LBTR.REVOCABLE_WHILE_QUEUED, which models only the participant's own revocation.
- Article
IN-36-005 numeral 18- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
must_be_true- Parameter
central_bank_may_cancel_unsettled_order- Value
true- Source
- IN-36-005 p.11 numeral 18
LBTR.END_OF_DAY_QUEUE_FLUSHIN-36-005 numeral 25major
At the hour set in the daily operating cycle, and before closing LBTR operations, the Central Bank may remove from the queue instructions dated for settlement that day which the issuer has not already cancelled. That removal is deemed equivalent to revocation by the issuing participant, and the Central Bank bears no liability for the consequences.
- Article
IN-36-005 numeral 25- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
must_be_true- Parameter
queued_order_flushed_at_close_is_revoked- Value
true- Source
- IN-36-005 p.13 numeral 25 and Parrafo. The HOUR is set by the daily operating cycle, which the instructivo defers to a Circular of the Gerencia the Lab has not read (numeral 95), so the flush is an operation the scenario invokes rather than a clock event the Lab invents.
LBTR.ORDER_STATEIN-36-005 numeral 19major
The state of a funds-transfer instruction in the LBTR is one of Almacenado, Anulado, Ingresado, Liquidado, Listo, Pendiente or Rechazado.
- Article
IN-36-005 numeral 19- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
must_be_member- Parameter
lbtr_order_state- Value
["Almacenado","Anulado","Ingresado","Liquidado","Listo","Pendiente","Rechazado"]- Source
- IN-36-005 p.11 numeral 19 a-g. The simulator's own four statuses map onto these: queued is Listo, settled is Liquidado, revoked is Anulado, rejected is Rechazado. The mapping is the Lab's, the vocabulary is the instructivo's.
Net settlement 2
NET.SETTLES_IN_LBTRArt. 79critical
The results of each clearing cycle of the electronic-instrument systems settle in the real-time gross settlement system.
- Article
Art. 79- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
cycle_settled_in_lbtr- Value
true- Source
- Reglamento Art. 79; Art. 67 for deferred-net finality
NET.DEFERRED_FINALITYArt. 67major
A deferred-net system carries its own irrevocability and finality rules for the settled net result.
- Article
Art. 67- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
net_result_final_once_settled- Value
true- Source
- Reglamento Art. 67
Pagos al Instante BCRD 10
PAI.FINAL_CREDITBCRD Pagos al Instantecritical
A Pagos al Instante BCRD transfer reaches the beneficiary as a final credit within eight minutes.
- Article
BCRD Pagos al Instante- Instrument
- BCRD published description of Pagos al Instante BCRD
- Resolution
- BCRD Pagos al Instante BCRD page
- Date
2026-09-04- Check
max_duration- Parameter
final_credit_minutes- Value
8 m- Source
- BCRD Pagos al Instante BCRD page; runs on the LBTR
PAI.OPERATING_WINDOWBCRD Pagos al Instantemajor
Available seven days a week from 07:00 to 23:00 local time, with a pause on weekdays between 16:00 and 18:30. An order given outside that schedule is not refused: it becomes effective at 08:00 on the next business day (PAI.DEFERRED_TO_NEXT_BUSINESS_DAY).
- Article
BCRD Pagos al Instante- Instrument
- BCRD published description of Pagos al Instante BCRD
- Resolution
- BCRD Pagos al Instante BCRD page
- Date
2026-09-04- Check
within_window- Parameter
instruction_instant- Value
"07:00-23:00 with a weekday pause 16:00-18:30"- Source
- BCRD Pagos al Instante page, https://www.bancentral.gov.do/a/d/2665-descripcion. Verified against the page on 2026-09-05.
PAI.DEFERRED_TO_NEXT_BUSINESS_DAYBCRD Pagos al Instantemajor
Orders instructed outside the Pagos al Instante schedule «se hacen efectivos a las 8:00 a.m. del siguiente dia laborable»: they become effective at 08:00 on the NEXT BUSINESS DAY. A Friday-night order therefore becomes effective on Monday morning, not on Saturday.
- Article
BCRD Pagos al Instante- Instrument
- BCRD published description of Pagos al Instante BCRD
- Resolution
- BCRD Pagos al Instante BCRD page
- Date
2026-09-05- Check
must_equal- Parameter
deferred_execution_local_time- Value
"08:00"- Source
- BCRD Pagos al Instante page, https://www.bancentral.gov.do/a/d/2665-descripcion, read on 2026-09-05: «se hacen efectivos a las 8:00 a.m. del siguiente dia laborable». This CORRECTS the Lab's earlier reading, which deferred to 07:00 on the next calendar day - two mistakes in one: the wrong hour, and a calendar day where the page says a business day.
- Notes
- The page says «fuera del horario» without distinguishing the after-hours case from the weekday pause, so the Lab applies the same deferral to both. Treating the 16:00-18:30 pause as «fuera del horario» is the Lab's reading and not the page's words; see assumption A-10.
PAI.FIXED_FEE_ONLYBCRD Pagos al Instantemajor
The fee for a Pagos al Instante transfer is a fixed amount; a percentage of the transferred amount is not permitted.
- Article
BCRD Pagos al Instante- Instrument
- BCRD published description of Pagos al Instante BCRD
- Resolution
- BCRD Pagos al Instante BCRD page
- Date
2026-09-04- Check
must_be_true- Parameter
fee_is_fixed_amount- Value
true- Source
- BCRD Pagos al Instante BCRD page
PAI.NO_DEDUCTION_BY_RECEIVING_BANKBCRD Pagos al Instantecritical
The beneficiary's institution may not deduct anything from the amount transferred.
- Article
BCRD Pagos al Instante- Instrument
- BCRD published description of Pagos al Instante BCRD
- Resolution
- BCRD Pagos al Instante BCRD page
- Date
2026-09-04- Check
must_be_false- Parameter
receiving_institution_deducted- Value
false- Source
- BCRD Pagos al Instante BCRD page; see also Reglamento Art. 81
PAI.FEE_REVERSED_ON_MISSED_DEADLINEIN-36-005 numeral 34 Parrafomajor
Where the transfer or payment is not applied within the established period for reasons attributable to the originating or the receiving entity, the originating client receives an automatic reversal of the fee charged, borne by the entity in breach.
- Article
IN-36-005 numeral 34 Parrafo- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
must_be_true- Parameter
fee_reversed_when_deadline_missed- Value
true- Source
- IN-36-005 p.16 numeral 34 Parrafo. The "established period" is the eight minutes of PAI.FINAL_CREDIT, whose own anchor remains assumption A-10 - so the consequence is the instructivo's and the deadline it hangs on is still partly the Lab's.
PAI.CLAIM_RESPONSE_OPERATIONALIN-36-005 numeral 36 a)major
Where a participant makes a Pagos al Instante claim against another participant, the latter answers within up to two business days for requests attributable to the participants' own operational or technological errors.
- Article
IN-36-005 numeral 36 a)- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
max_count- Parameter
claim_response_business_days- Value
2 business days- Source
- IN-36-005 p.17 numeral 36 a). Counted in BUSINESS days by the run's own calendar, which is Lab configuration (assumption A-01), so the count is only as right as the holiday list.
PAI.CLAIM_RESPONSE_CLIENTIN-36-005 numeral 36 b)major
The same claim mechanism allows up to five business days for requests arising from claims made by the client.
- Article
IN-36-005 numeral 36 b)- Instrument
- Instructivo del Sistema de Liquidacion Bruta en Tiempo Real
- Resolution
- IN-36-005 version 03, aprobado por el Gobernador del Banco Central
- Date
2026-04-09- Check
max_count- Parameter
claim_response_business_days- Value
5 business days- Source
- IN-36-005 p.17 numeral 36 b); the same calendar caveat as PAI.CLAIM_RESPONSE_OPERATIONAL
PAI.USD_NO_CORRESPONDENTBCRD Pagos al instantemajor
Transfers in United States dollars through Pagos al Instante BCRD move between participants without a correspondent bank: they settle in the LBTR, which settles in Dominican pesos, US dollars and euros.
- Article
BCRD Pagos al instante- Instrument
- BCRD published description of Pagos al Instante BCRD
- Resolution
- BCRD Pagos al instante page
- Date
2026-09-04- Check
must_be_false- Parameter
correspondent_used- Value
false- Source
- BCRD Pagos al instante page; BCRD Sistema LBTR page for the settlement currencies (Art. 56, national and foreign currency)
PAI.ORIGINATOR_CONVERTSBCRD Pagos al instantemajor
Where the beneficiary's account is held in a currency other than the one the order is instructed in, the originating institution may perform the conversion, and the beneficiary is credited in the currency of their own account.
- Article
BCRD Pagos al instante- Instrument
- BCRD published description of Pagos al Instante BCRD
- Resolution
- BCRD Pagos al instante page
- Date
2026-09-04- Check
must_be_true- Parameter
originating_institution_converted- Value
true- Source
- BCRD Pagos al instante page. Whether the conversion is a permission or an obligation is read here as a permission: the rule fires only when a conversion was actually needed.
QR 1
QR.PRESENT_AND_READArt. 78 SIVmajor
Every payment-service provider must be able to present and read a QR code carrying the data needed to initiate a payment order, to the standard the Central Bank sets by instructivo.
- Article
Art. 78 SIV- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
manual- Parameter
qr_present_and_read- Value
true- Source
- Reglamento Art. 78 numeral IV; the QR instructivo is pending publication
Records 2
RECORDS.RETENTION_YEARSArt. 41-42major
Providers and participants store every transaction, including rejected ones and adjustments, for ten years, with full user access and delivery to the Central Bank and supervisors without undue delay.
- Article
Art. 41-42- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
retention- Parameter
retention_years- Value
10 years- Source
- Reglamento Art. 41-42
RECORDS.INCLUDES_REJECTEDArt. 41major
Rejected transactions and adjustments are part of the retained record, not only settled ones.
- Article
Art. 41- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
rejected_transactions_recorded- Value
true- Source
- Reglamento Art. 41
SGPI instant payments 5
SGPI.FINAL_CREDITBCRD SGPI pagecritical
The Sistema de Gestion de Pagos Instantaneos credits the beneficiary finally within ten seconds of the instruction.
- Article
BCRD SGPI page- Instrument
- BCRD published description of the SGPI
- Resolution
- BCRD SGPI page
- Date
2026-09-05- Check
max_duration- Parameter
final_credit_seconds- Value
10 s- Source
- BCRD SGPI page, https://www.bancentral.gov.do/a/d/6142. Verified on 2026-09-05. THE TEN SECONDS ARE NOT IN THE REGLAMENTO. Arts. 60-61 establish the SGPI, its administration and who must participate in it; the ten-second parameter is published on the Central Bank's own page and nowhere in the articles. The citation was corrected on 2026-09-05, because a threshold with an article beside it that the article does not carry is exactly the fabricated regulatory fact this catalogue exists to prevent.
- Notes
- Where the ten seconds is measured from and to is still the Lab's reading (assumption A-09); what is settled is where the figure comes from.
SGPI.AVAILABILITYBCRD SGPI pagecritical
The SGPI operates 24/7/365; there is no operating window and no cut-off.
- Article
BCRD SGPI page- Instrument
- BCRD published description of the SGPI
- Resolution
- BCRD SGPI page
- Date
2026-09-05- Check
must_be_true- Parameter
available_at_instruction_time- Value
true- Source
- BCRD SGPI page, https://www.bancentral.gov.do/a/d/6142. Verified on 2026-09-05. Like the ten seconds, the 24/7/365 availability is published on the page and is not an article of the Reglamento; the citation was corrected on 2026-09-05. It is CONFIRMED independently by IN-36-005 pp.6-7 numeral 3 mm), which defines the SGPI as operating in real time, 24 hours a day, 7 days a week.
SGPI.MANDATORY_PARTICIPATIONArt. 61 SIcritical
All financial-intermediation entities, electronic-payment entities, acquirers and sub-acquirers are mandatory participants of the SGPI.
- Article
Art. 61 SI- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
participant_registered_in_sgpi- Value
true- Source
- Reglamento Art. 61 numeral I
SGPI.NOTIFY_BOTH_PARTIESArt. 61major
Payer and beneficiary are both notified immediately once the instant payment is credited.
- Article
Art. 61- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
both_parties_notified- Value
true- Source
- BCRD SGPI page; Reglamento Art. 61
SGPI.ALIAS_ADDRESSINGArt. 60-61major
An instant payment may be addressed to an alias resolved by the Central Bank's directory.
- Article
Art. 60-61- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
alias_resolved- Value
true- Source
- BCRD SGPI page
SIPA regional transfers 2
SIPA.OPERATING_WINDOWBCRD SIPAmajor
Regional real-time USD transfers with Costa Rica, El Salvador, Guatemala, Honduras and Nicaragua run on weekdays from 07:00 to 17:00 Dominican time.
- Article
BCRD SIPA- Instrument
- BCRD published description of the SIPA
- Resolution
- BCRD SIPA page
- Date
2026-09-04- Check
within_window- Parameter
instruction_instant- Value
"weekdays 07:00-17:00"- Source
- BCRD SIPA page
SIPA.ORIGINATOR_FEEBCRD SIPAmajor
A SIPA transfer carries a US$5 fee, paid by the originator.
- Article
BCRD SIPA- Instrument
- BCRD published description of the SIPA
- Resolution
- BCRD SIPA page
- Date
2026-09-04- Check
must_equal- Parameter
originator_fee_usd- Value
5 USD- Source
- BCRD SIPA page
SIPARD participation and interoperability 3
SIPARD.DIRECT_PARTICIPANT_HAS_CB_ACCOUNTArt. 4 zz-bbbcritical
Direct participants in the SIPARD hold current accounts at the Central Bank; indirect participants settle through a direct one.
- Article
Art. 4 zz-bbb- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
has_central_bank_current_account- Value
true- Source
- Reglamento Art. 4 zz-bbb; BCRD SIPARD page
SIPARD.INTEROPERABILITYArt. 10major
Every administrator of a payment system must be interoperable.
- Article
Art. 10- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
manual- Parameter
administrator_is_interoperable- Value
true- Source
- Reglamento Art. 10
SIPARD.SGPI_MANAGED_BY_CENTRAL_BANKArt. 62critical
The SIPARD is a public service owned exclusively by the Central Bank; SGPI management is not delegable.
- Article
Art. 62- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_true- Parameter
sgpi_administrator_is_central_bank- Value
true- Source
- Reglamento Art. 60, 62; Ley 183-02 Art. 27
Transfers 1
TRANSFER.NO_CHARGE_TO_BENEFICIARYArt. 81; Art. 56 SIIcritical
No charge may be applied to the beneficiary of a transfer or payment; the cost is borne by the ordering party.
- Article
Art. 81; Art. 56 SII- Instrument
- Reglamento de Sistemas de Pago
- Resolution
- Segunda Resolucion de la Junta Monetaria
- Date
2025-08-28- Check
must_be_false- Parameter
charge_applied_to_beneficiary- Value
false- Source
- Reglamento Art. 81 and Art. 56 numeral II
9Versioning and change policy
Three things carry a version, and they move for different reasons.
| What | Where you read it | When it moves |
|---|---|---|
| The rule catalogue | ruleCatalogue on /v1/health, and on every run report | a rule is added, a value is adjusted, or a citation is corrected. Dated, YYYY.MM.DD. |
| The OpenAPI contract | info.version in /openapi.json | an endpoint, a field or a response shape changes |
| This manual | the footer of every page | regenerated from the sources on every deploy; it cannot be older than what it documents |
What the Lab will and will not do to you
- Additive changes arrive without warning, and your integration should tolerate them: a new endpoint, a new field on a response, a new rule in the catalogue, a new scenario, a new actor on the bus. Ignore fields you do not recognise.
- A breaking change to a request or a response is announced first, in the changelog and to every team holding a key, with the date it takes effect.
- A regulatory value changing is not a breaking change. If the Junta Monetaria adjusts a cap,
or the CPI moves the high-value threshold, the catalogue changes and your runs start failing where they used to
pass. That is the ecosystem working. The rule's
resolutionDatetells you which vintage you are being judged against. - A world reset is never done to you. Only your own team can reset your world, and an admin only on request.
Reproducibility across a change
A run report carries the rule-catalogue version, the seed, the clock mode and the fidelity tier of every actor.
A run that passed under catalogue 2026.09.05 is a pass on that catalogue, and the report
says so on its face. That is what lets a report be handed to a supervisor months later and still mean something
precise.
10Limits and fair use
The Lab pays for this instance out of its own budget. There is no billing, no quota page and no plan to add one - what there is instead is an expectation, stated plainly.
What the instance is
| Compute | A single small Cloud Run service that scales to zero. The first call after an idle period pays a cold start of a few seconds. |
| Your world | Built on your first call and kept in memory for the life of the instance. A world does not survive a redeploy or a scale-to-zero - it is rebuilt from the same seed, so the people and their documents come back, and anything you did does not. Treat a world as a working surface, and the run report as the record. |
| Your reports | In the project's Firestore, and they do survive. They are the record, and the seed on them is what makes a run reproducible. |
| Concurrency | A run holds a world while it executes. Several teams running at once is fine; one team firing hundreds of concurrent runs is not, and will simply be slow for that team first. |
What we ask
- Do not load-test the instance. It is not a performance rig and its timings are Lab configuration, not measurements of anything. If you want to exercise throughput, ask for a sponsored node.
- Do not poll
/v1/runsin a tight loop. A run tells you its id when it finishes; webhooks exist for the rest. - Do not put a real credential, a real PAN, a real document number or real personal data into this ecosystem. There is nowhere for it to go that is appropriate, everything is recorded in a trace, and nothing here is a system of record for anybody's data. This is the one limit that is not about politeness.
- Do not present a run as an approval. See the governance section; it is short and it matters more than everything else on this page.
- One key, one team. If a second team in your organization needs access, ask for a second key so that the two get separate worlds. Sharing a key means sharing a world, and then two teams debug each other's state.
11Support, and asking for more
Two forms, and they go to the same place
- Request a team key - for an organization that wants to plug something in. Tell us who you are, what you would exercise and roughly when; the Lab mints a key and a world for you and hands the key over. Nothing is issued automatically, and that is on purpose.
- Comments, suggestions, corrections and defects - including «this rule cites the wrong article», which is the single most valuable thing anybody can send us. Name the article or the document, and we will check it against the source and correct the catalogue.
By address
| For | Write to |
|---|---|
| partnerships, keys, sponsored nodes | partners@cemi.ai |
| a leaked key, or anything security-shaped | security@cemi.ai |
| legal and governance questions | legal@cemi.ai |
| anything else | contact@cemi.ai |
A sponsored node
A sponsored node is an instance of the ecosystem carrying one institution's own configuration: its personality, its policies, its rates, its own private lane for its vendors and the startups it works with. The Lab's shared instance deliberately carries fictitious exchange rates and Lab thresholds, because a shared instance is the wrong place for a real table. A sponsored node is the right place, and it is the mechanism by which a bank, a processor or a regulator gets an ecosystem shaped like their own.
Ask through the key-request form, saying so in the intended-use field, or write to partners@cemi.ai.
The Lab
The Virtual Financial Ecosystem Simulation is the fourth proving ground (ES: banco de pruebas) of CEMI's Financial Innovation Lab, alongside Intelligent Machines, the Open Sandbox and the Living Lab.
12Governance
This is NOT an «ambiente de prueba» under Art. 83 of the Reglamento de Sistemas de Pago. The ecosystem involves no real providers and no real external users, so it does not fall under the no-objection regime. A run is never a substitute for the Central Bank's no objection. Where your next step is an Art. 83 request, a run report is evidence for that request and nothing more.
What a report is, and what it is not
A run report says what was tested: the scenario, the variant, the seed, the fidelity tier of every actor, the clock mode and the rule-catalogue version. A «pass» is a pass on that run, against that catalogue, at those fidelity tiers.
- It is evidence you exercised a process end to end against the rules as the Lab has catalogued them, reproducible by anybody holding the seed.
- It is not a certification, an authorization, a no objection, or a statement that any institution has reviewed anything. No institution being modelled here has endorsed this ecosystem.
The rule the whole catalogue is built around
A rule you cannot cite an article for is not a rule. It is either an assumption - and belongs
in the numbered assumptions with a reason and a statement of what would settle it - or a Lab control,
which goes into the catalogue with article: "Lab control" and a source saying plainly that it is not
a regulatory statement. Borrowing a nearby article that does not say the thing is the one move that is never
available.
No real personal data
The synthetic population is generated from the run seed and its generator is auditable. Nothing in it corresponds to a real person, and the identifier scheme is built so that nothing in it can be mistaken for one. Do not put real personal data in.
Where the values come from
Every regulatory figure in the catalogue is traceable to a published instrument: the Reglamento de Sistemas de Pago approved by the Segunda Resolución de la Junta Monetaria of 28 August 2025, and the Central Bank's instructivos IN-36-005, IN-36-023 and IN-36-024. Where a value had to be chosen because code cannot defer, it is recorded as a numbered Lab assumption and never dressed up as regulation. If you find a citation that does not say what we claim it says, tell us - the feedback form has a category for exactly that.
13Changelog
Newest first. A dated entry here is the notice a breaking change gets.
2026-09-05 - The console and the intake forms speak three languages
- The console is trilingual. English, Spanish and French, switched in the masthead
and kept across a reload. Every visible string comes from one dictionary
(
console/i18n.js); dates, figures and money are formatted withIntlfor the active locale, so a French reader sees20 000,00 RD$where an English one seesRD$20,000.00. The language can be pinned with?lang=en|es|fr, which is what a demo link should carry. - Both intake forms are published in French as well as English and Spanish, at
/request-key/fr/and/feedback/fr/, generated from the same vocabularies the gateway validates against. - A refused form now answers with codes, not prose. The
errorsarray of a422carries stable identifiers -organization-required,email-required-to-answer,reference-required- and the page renders each one in the language its reader is already reading. If your integration matched on the English sentences, match on the codes instead. - Sign-in falls back to a redirect when the browser refuses the pop-up, which is what a locked-down corporate profile does.
- This manual is still English and Spanish; the French edition is next, and the navigation says so.
2026-09-05 - Teams, la Población del Lab, the instructivos, and three corrections
- Correction, Pagos al Instante. An order given outside the schedule becomes effective at 08:00 on the next BUSINESS day - «se hacen efectivos a las 8:00 a.m. del siguiente día laborable», from the Central Bank's own page. The Lab had been deferring to 07:00 on the next calendar day: the wrong hour and the wrong kind of day. A Friday-night order was executing on Saturday morning and now waits until Monday, which is the whole weekend. If your integration asserted the old behaviour, this will change your results.
- Correction, the SGPI parameters. The ten-second final credit and the 24/7/365 availability are published on the Central Bank's SGPI page and are not articles of the Reglamento. The catalogue had been citing Arts. 60-61 beside both figures; those articles establish the SGPI and say who must participate in it, and carry neither number. Both rules now cite the page. A threshold with an article beside it that the article does not carry is exactly the fabricated regulatory fact this catalogue exists to prevent, and this was one.
- New rule, Art. 22. An electronic-payment entity may hold the funds backing its managed balances only in its Central Bank current account, or in Central Bank or Ministerio de Hacienda securities pledged in favour of the Central Bank - never in a commercial bank. That is a different question from whether the float covers the balances, and a float can satisfy one and breach the other.
- A finding now carries the
instrumentthe article belongs to, becauseArt. 22andIN-36-005 numeral 22are both articles and only the instrument says which document a reader has to open. - One world per team. A team key now carries its team, and each team gets an isolated ecosystem: its own seed, clock, ledgers and alias directory. A team lists its own runs and nobody else's.
- A team key opens the whole lane. Previously it opened only the machine lane on a signed-in instance, which meant a headless integration could not drive the control plane at all. The restriction existed because a key was a team and a team was isolated from nothing; a key now reaches exactly one world, so it is gone. This is the one behaviour change that could affect an existing integration, and it only widens what a key can do.
- La Población del Lab. Every world is born with three banks, an EPE, the DD/DC administrator,
four machines, 200 people, 40 businesses, accounts, cards, aliases and twelve flagged adversaries.
GET /v1/directoryhands you the whole of it. - New endpoints:
GET /v1/directory,POST /v1/worlds/reset,GET /v1/worlds. - 26 rules adopted from the BCRD instructivos, taking the catalogue from 48 to 76 with the two corrections above: the
RD$15,800,000 high-value threshold, the LBTR priority model and settlement prelation, the end-of-day queue flush,
the electronic-payment-account funding-availability ceilings, the calendar-day cap window, the full-reserve
float, the no-overdraft rule and the claim deadlines. Rules from the two 2021 instructivos carry
needsRecheck. - This manual, in English and Spanish, generated from the sources.
2026-09-04 - Deployed
- The Lab's own instance on the
financial-ecosystemproject: the gateway on Cloud Run, the console on Firebase Hosting, reports in Firestore. - Console sign-in with Firebase Auth, and an allow-list deciding who may start a run.
- Reports behind a backend, so a report survives an instance restart.