Changelog

Contract releases with what integrators must do, newest first. Current contract: 1.0.0-draft.14. Deprecated operations are listed on Deprecations.

API contract

1.0.0-draft.14

(2026-09-24, Phase 10 close-out review: 526 paths, 660 operations, 849 schemas). One additive enum value for integrators; the rest tightens staff operations:

  • Neutral review holds. Hold.subject.type adds payment_review: funds reserved while a payment is processed (for example an incoming credit under review). Its subject.id is the hold's own id and its description is neutral; the amount counts against the available balance like any other hold. Clients that switch on the subject type should treat unknown values as a generic reservation.
  • Step-ups on policy writes (staff). PUT /operators/{operatorId}/settings needs the mfa.change step-up when it changes security, approvedEmailDomains or apiKeyInvitationDomains. PUT /platform/compliance/defaults needs the new capability platform.compliance, an X-Access-Reason and the compliance.gate_settings_change step-up.
  • Status unsubscribe limit. POST /public/status/subscriptions/unsubscribe is limited per unsubscribe token (5 an hour), not per address, so mail providers' one-click unsubscribes are never refused for sharing an address.
  • New capability. platform.compliance (WireBloom super admins).

Also in draft.14 (2026-09-25, Phase 11/12 residual wave: 529 paths, 664 operations, 856 schemas). Additive: no operation, schema or property was removed and no request field became required. Clients with strict enums or strict status-code handling should read the 202 items:

  • Batch id look-ups (filter[id]). GET /recipients, /customers, /ops/customers, /balances and /platform/operators accept filter[id]=<id>,<id>,… (at most 100; above that 400 too_many_filter_values) and return only those rows; unknown or foreign ids are absent. Send limit ≥ the number of ids.
  • Counts (withTotal). GET /approvals gains withTotal (already served by /recipients, now declared); limit=1&withTotal=true is the count-only form for badges. On every list, page.total is computed on the first page only (a request without cursor).
  • Conditional GET (ETag). GET /notifications, /notifications/unread-count, /approvals and /documents return a weak ETag; send it back as If-None-Match when polling and the API answers 304 without a body while nothing changed.
  • Processing ETA. Payment.processing (PaymentProcessing: status queued, position, etaSeconds, estimatedAt) is present while an approved, due payment waits in the operator's submission queue, and absent once submitted.
  • Approval-policy dual control. ApprovalSubjectType adds approval_policy_change. PUT /customers/{customerId}/settings/approval-policies/{kind} answers 202 with the unchanged live policy and ApprovalPolicy.pendingChange when the edit loosens control and another owner or admin exists; that person decides it through /approvals/{id}. Approval.subjectSummary adds recipientMaskedIdentifier.
  • Batch-rail imports under four eyes (staff). POST /ops/batch-rail/connections/{connectionId}/imports answers 202 with a StaffApproval (batch_import) when four eyes apply (always for a production connection); POST …/imports/{fileId}/statement reconciles the credits of a proposed import against the bank statement before it can be approved.
  • Operator offboarding under four eyes (platform). POST /platform/operators/{operatorId}/offboard answers 202 with an OperatorOffboardRequest on the first platform user's call; a different platform user's call within 24 hours offboards (200).
  • OAuth consent. GET /auth/oauth2/authorize no longer mints a code: with a web session it redirects to the web consent page, which confirms with the new POST /auth/oauth2/authorize (OAuthConsentRequest → OAuthConsentResponse.redirectUrl). Mobile apps using the system-browser flow need no change beyond the one consent tap.
  • Also new. GET /ready (readiness; 503 while the instance cannot serve, GET /health stays liveness), POST /billing-runs/previews (billing dry run as a job, 202 + Job), six step-up actions and x-step-up on every platform write and on the remaining operator money and policy writes, the chain and roll-up fields of AuditChainStatus, a 92-day window for free-text audit search (q), 202 from POST /me/email-change whether or not the address is in use, and per-IP 429s on anonymous routes and inbound provider webhooks.

1.0.0-draft.13 (2026-09-24)

Phase 10 wave 3: 525 paths, 659 operations, 849 schemas

All additive for integrators except the new step-ups on key revocation and webhook changes; staff endpoints need operator staff sessions unless an operator-key scope is listed:

  • Developer portal and published contract (P9-T07). GET /v1/openapi.json (tag public-api, no authentication) serves this contract as JSON in every environment with Cache-Control: public, max-age=300, a strong ETag (304 on If-None-Match) and X-Contract-Version. Swagger UI at /docs stays internal (DOCS_ENABLED); in production /docs redirects to https://developers.wirebloom.com/reference. A deprecated operation's Link: <…>; rel="deprecation" points to https://developers.wirebloom.com/deprecations#{operationId}; deprecations can be declared in the contract (deprecated: true with x-deprecated-at and x-sunset, optional x-deprecation-link). Problem type URIs (https://api.banking.wirebloom.com/problems/{slug}) redirect to https://developers.wirebloom.com/errors#{slug}.
  • API usage and integration home (P9-T11). Customers: GET /developer/api-usage (daily usage per key: requests, client and server errors, rate-limit hits, average duration, last use; from/to up to 90 days, apiKeyId, environment) and GET /developer/api-keys/{apiKeyId}/requests (the last 100 requests of a key over 7 days with a status breakdown; method, route template, status, duration and request id only, never bodies), api-keys.manage. Operator staff: GET /integration, GET /integration/api-usage and GET /integration/api-keys/{apiKeyId}/requests (integration.view, operator owners and admins; also operator-level keys with integration:read): banking connections with health, breaker, webhook registration, backlog, reconciliation and checklist progress, key counts, usage across every key, integrator webhook health and portal links. A call appears within about 5 seconds.
  • Operator-level API keys and webhooks (P9-T08b, §2.1, §8.1, §8.2). /api-keys and /integrator-webhooks are x-tenant: any; ApiKey.level; ApiScope adds customers:read, customers:write, onboarding:read, treasury:read, reports:read, integration:read; the operator routes listed in §2.1 declare x-api-key-scopes. IntegratorEventType adds the six operator-only types; IntegratorEvent adds customerId and data.related; data.object adds customer and compliance_hold. Step-ups: DELETE /api-keys/{id} needs api_key.revoke; creating, updating, deleting and rotating the secret of an integrator webhook endpoint need integrator_webhook.change (sessions only; API keys are not subject to step-up).
  • SDKs and Postman (P9-T08, §13). TypeScript and Python SDKs, generated C#, Java, PHP and Go clients, shared webhook test vectors and a Postman collection with environments, all versioned 1.0.0-draft.13.
  • Developer programme and simulations (P9-T09). Public sign-up for an operator's sandbox: GET /developer/registration-options?operator=<slug>, POST /developer/registrations (always 202) and POST /developer/registrations/verify (201 with the workspace and the first wb_test_ key). GET /developer/workspace and GET /simulations (balances:read) plus the simulation operations under /simulations/* (sandbox keys only). Staff: /ops/developer-programme, /ops/developer-invitations, /ops/developer-registrations* (developers.manage) and /ops/simulations/provider-webhooks. Me.developerWorkspace tells a client the active customer is a sandbox workspace; BrandingPublic.slug gives the operator slug for the sign-up link.
  • Status page and support (P9-T10). GET /public/status and /public/status/subscriptions* (no authentication, double opt-in), GET /status (any member: the platform page and the operator's private page), staff /ops/status* (status.manage: incidents, updates, component overrides, subscribers). Support requests: POST /support/requests, GET /support/requests[/{id}] (any member, self.profile) and the staff inbox /ops/support-requests* (support.manage).
  • VIBAN pools, partner assets and account identifiers (P10-T04, P10-T06). Customers: GET /account-identifiers (the customer's VIBANs, including identifiers closing after a re-issue or provider switch) and GET /account-identifiers/{identifierId}/letter (PDF details letter), details.view. New customer-level event balance.details_changed: a balance's account identifier changed (data.status = reissue, provider_switch or closing); fetch GET /account-identifiers for the new details and tell your payers. Staff (operator.providers): /ops/viban-pools*, /ops/vibans/{vibanId}/{close,reissue,currencies}, /ops/viban-detail-changes* and GET /ops/partner-assets (with filter[expiringWithinDays]); new step-ups viban.pool_change, viban.lifecycle and partner_assets.change.
  • Provider costs, packs and open banking (P10-T05, P10-T08). Staff: /ops/provider-costs* (provider-costs.view / provider-costs.manage: costs, margin, imports, ingest, monthly close), /ops/resolution-packs* (resolution-packs.manage) and /ops/regulatory-packs* (regulatory-packs.view / regulatory-packs.generate); downloads need the step-ups resolution_pack.download / regulatory_pack.download. Customers: GET /connected-apps, GET /connected-apps/{appId} (open-banking.view) and POST /connected-apps/{appId}/revoke (open-banking.manage), the third-party apps with open banking consent on the customer's accounts.
  • New capabilities. integration.view, developers.manage, status.manage, support.manage, platform.status, provider-costs.view, provider-costs.manage, resolution-packs.manage, regulatory-packs.view, regulatory-packs.generate, open-banking.view, open-banking.manage (hyphenated names; clients match them as opaque strings).

1.0.0-draft.12 (2026-09-24)

Phase 10 wave 2: 426 paths, 543 operations, 703 schemas

All additive for integrators; the new staff endpoints need operator staff sessions:

  • Documents requested inbox (customers). GET /document-requests, GET /document-requests/{requestId} and POST /document-requests/{requestId}/fulfil (tag activity, capabilities document.view / document.upload) are the one inbox for every document the operator asks for, whether for a partner compliance case or enhanced due diligence. DocumentRequest carries kind, title (null for partner-case requests: show the kind), documentTypes, message, status (requested, fulfilled, cancelled), dueAt, customerNote and the uploaded documents; never the case or its reason. Each request is announced by the generic document_requested notification. Upload with POST /documents first, then fulfil with the document ids (422 document-not-clean until the scan passed). The interim /compliance/document-requests routes of the P10-T02 drafts were never released and are removed.
  • Partner compliance cases (staff). /ops/cases* (tag operator-ops; cases.view, cases.respond, cases.approve): Banking Circle RFIs and inbound recall requests with their timeline, links, notes, documents, customer document requests, RFI answers, a four-eyes recall decision and POST /ops/cases/sync.
  • Compliance-as-a-service (staff). /ops/compliance/* (holds, queue, report, screening hits, recipient screening and rescreen, monitoring suppressions, gate settings, EDD cases and the review of documents customers provided), POST /ops/compliance/escalations, /ops/mlro/* (nominated officers, suspicion reports, DAML) and /platform/compliance/defaults (tag compliance; compliance.view, compliance.manage, compliance.decide, mlro.decide). Payments held by the compliance gate stay on_hold until staff release or block them.
  • Integrated Finance sync (staff). GET /provider-connections/{connectionId}/sync, GET …/sync/links, POST …/sync/backfill, POST …/sync/drift and POST …/sync/links/{linkId}/resolve (tag providers, operator.providers).
  • New step-up actions. cases.recall_decision, compliance.hold_decision, compliance.screening_decision, compliance.suppression, compliance.edd_decision, mlro.designation, mlro.sign_off; the sync backfill, drift and conflict resolution use provider_connection.change.
  • New enum values. PaymentExceptionKind adds compliance_hold; reconciliation exception kinds add sync_conflict; staff approval kinds add compliance_hold_release. Clients with a strict enum must accept them.
  • Tipping-off. Staff-written customer text (partner-case and EDD document requests) is checked by one shared guard and refused with 422 error code tipping_off.

1.0.0-draft.11 (2026-09-24)contains breaking changes

provider platform parity; review W1-20

  • Customer types. Customer.customerType is business, sole_trader or individual (POST /customers takes it; identityType is derived: corporate for businesses, individual otherwise). A legacy create with identityType: individual becomes customerType: individual and its registrationNumber is dropped. Sole traders and individuals complete the personal section of KybCase (names, date of birth, nationality, residential address) instead of the company, address and ownership steps; an individual sends no company section and a sole trader only tradingName, incorporationCountry, industryCode and website (409 otherwise). The verified identity (names, date of birth, nationality) cannot change once the identity check started (409 identity_locked). Clients that switch on identityType keep working; clients that treat every customer as a company must handle the two new types.
  • Metadata. Payments, recipients, balances and customers carry metadata: at most 20 keys ([A-Za-z0-9_-]{1,40}), string values of at most 500 characters, 8 KiB in total. List operations that support it take up to 5 filter[metadata.<key>]=<value> parameters (ANDed). Metadata is integrator-owned and unverified: never base authorisation decisions on it.
  • Rails. New rail values target2, ach, wire and eft, offered only when the operator's active connection reports them. Clients with a strict Rail enum must accept the new values.
  • Purpose codes. GET /payments/purpose-codes lists the catalogue per currency, destination and rail. Payments in CNH must carry one of GOD, STR, CTF, OTF (422 purpose_required / invalid_purpose). Elsewhere purposeCode is optional and any ISO 20022 ExternalPurpose1Code (4 letters) is accepted; a partner whose rail only takes a closed list drops a code outside it rather than failing the payment. Free text that is not a 4-letter code is refused (422 invalid_purpose).
  • Routing codes. Recipient.bank.routingCodes takes cnaps, bankCode and branchCode (with country requirement sets for CN and other markets in GET /recipients/requirements).
  • Bulk submit stays asynchronous (202, draft.10).

Breaking changes for operator tooling (operator console and staff API keys only; integrator endpoints are unaffected):

  • ProviderConnection.status is the lifecycle ConnectionLifecycle (draft, configured, testing, active, draining, retired); disabled and error are gone (filter[status]=error answers 400). Health is healthy / lastHealth.
  • PATCH /provider-connections/{connectionId} no longer takes status (422, the body is strict). Go live with POST …/activate (activation checklist), stop new work with POST …/drain and retire with POST …/deactivate (step-up provider_connection.change; 409 connection-in-use while balances, settlement money, in-flight items or active mandates remain). POST …/test verifies but never activates.
  • POST /provider-connections refuses customerId (422): an operator has one active banking connection per environment.
  • A live connection's config change may not remove what its activation checklist required (for example the Banking Circle inboundIpAllowList in production) nor change Integrated Finance webhook keys (409); configured Integrated Finance keys may only add new key versions on sandbox connections (422 reserved_version / not_allowed).
  • New staff endpoints: GET …/activation, …/webhooks (registration, keys, subscriptions, test), POST …/webhook-token/rotate, and /ops/treasury/* (operator treasury payments and FX under dual control, approval with step-up treasury.approve).

1.0.0-draft.10 (2026-09-24)

POST /bulk-payments/{bulkPaymentId}/submit is asynchronous. It returns 202 with BulkPaymentSubmitAccepted (a Job plus the batch in submitting, header Location: /v1/jobs/{jobId}); poll GET /jobs/{jobId} for progress and GET /bulk-payments/{bulkPaymentId} for the final submitted / partially_submitted status and row results.

Platform

Notable changes to the platform as a whole (from the project changelog).

[Unreleased] — 2026-09-23 / 2026-09-24 (first commit 7ca6456 on main)

Added (99)
  • 2026-09-28: Staff customer creation, owner invitations and per-currency customer breakdown (spec superpowers/specs/2026-09-28-staff-customer-creation-and-currency-breakdown-design.md): POST /customers accepts no primaryOwner for console sessions (draft, no invitation, audit invitationId: null; API keys still get 422 /primaryOwner); POST/GET /ops/customers/{customerId}/owner-invitation (customer.create + people.invite step-up; 409 invalid-transition with an owner, 409 conflict when pending without resend; resend revokes the previous link; token and code never returned or audited; tenancy.customer.owner_invited); POST /platform/operators/{operatorId}/customers (super admins, X-Access-Reason, platform.change, runs in the operator's tenant context; tenancy.platform_access + tenancy.customer.created); OpsCustomerSummary.primaryOwner; GET /ops/accounts/{currency}/customers (keyset on the aggregated total, search, status/type filters) and GET /ops/accounts/{currency}/summary (live, coverageDrift = settlement − booked). Web: Add customer on /ops/customers and on the platform operator page, Owner card on the customer People tab, /ops/accounts/[currency] with summary strip and CSV export (10,000-row cap), currency links on /ops/accounts. Contract additive on 1.0.0-draft.14 (SDK regenerated: CustomerCreate.primaryOwner optional). Follow-ups N362–N368. Full gate: @wirebloom/db 251/251 and @wirebloom/api 1300/1304 (re-run standalone with --maxWorkers=2 after hitting Postgres max_locks_per_transaction under pnpm check's full turbo parallelism; api's remaining 4 (activity-keyset.e2e.test.ts, activity.e2e.test.ts) are pre-existing timeouts in code untouched by this feature, reproducing in full isolation, most likely from this session's own leftover ephemeral test databases rather than a regression), web 916/916, @wirebloom/providers 727/731 (known pre-existing gap: missing Banking Circle camt052/camt053 fixtures, N-tracked separately); generate:check, sdk:check and format:check clean.
  • 2026-09-28: Staff customer creation: final review fixes and deployment (P13-T02 merged to main 1d1132e, DigitalOcean deployment c7cd73e8 live on bank.wirebloom.com): the shared ops mutation hook now renews the Idempotency-Key after any stored 4xx other than 401/403/429, so a corrected resubmit no longer answers 422 idempotency-key-reused (the three new customer dialogs use it; the remaining ops dialogs are tracked as N369); owner-invitation accept and staff resend are mutually exclusive (accept locks the customer row and re-reads the invitation status in the same transaction; both the revoke and the accepted update are predicated on status = 'pending' with row-count checks, resend answers 409 when the invitation changed meanwhile; a revoked link can no longer become the owner membership; race e2e added); the currency breakdown CSV export always walks pages by legal name so it reconciles with the summary strip (N364 notes it is not a point-in-time snapshot); the Owner card shows an error state on query failure and refetches after a 409; the per-currency breakdown link on /ops/accounts is shown only with customer.view; GET /ops/accounts/{currency}/summary lists 400 in the contract. Gate: web 933/933, touched api suites 40/40, typecheck, generate:check and sdk:check clean.
  • 2026-09-28: Developer portal hosted on DigitalOcean (developers.wirebloom.com, N252): second App Platform app wirebloom-developers from platform/infra/digitalocean/portal.yaml (one portal service built from apps/portal/Dockerfile, no secrets, $5/month); tooling/deploy/do-app.sh accepts DO_APP_SPEC and DO_APP_NAME to drive it; the portal's API base URL is https://bank.wirebloom.com/v1 (the code samples use the same host until a sandbox environment exists). DEPLOYMENT.md §8.9, ENVIRONMENT.md §7 and DEVELOPER_PORTAL.md updated; the owner sets the developers CNAME at GoDaddy.
  • 2026-09-28: Customer registration and access approval (spec superpowers/specs/2026-09-28-customer-registration-design.md, D-58): /register for businesses, sole traders and individuals; tenant.customers.access_status with auto or staff approval (platform.platform_settings default, per-operator registration.approval), /ops/customers/registrations with approve / reject / reverse (POST /customers/{id}/access-decisions, step-up ops.kyb_decision), /pending holding page and reduced capabilities, 403 access-rejected at login; onboarding mode prepare | live per operator (KybCase.mode, 409 onboarding-not-open); email verification skipped behind a super-admin setting until Resend is active; templates registration_received, access_approved, access_rejected, ops_registration_pending; migration 0056; contract additive on draft.14 (/ops/registrations, /platform/settings, BrandingPublic.registration, Me.access, Customer.accessStatus)
  • 2026-09-28: KMS_LOCAL_ALLOW_PRODUCTION=true (owner opt-in, D-57 item 5) lets the local KMS backend run in production with MFA_MASTER_KEYS mandatory (packages/crypto, packages/auth, api identity config); the DigitalOcean app uses a software master key. packages/db bootstrap CLI for the first operator and owner user (N348). First owner created on production; bank.wirebloom.com live
  • 2026-09-28: DigitalOcean App Platform as primary hosting (D-57), AWS kept as the secondary option: app spec template platform/infra/digitalocean/app.yaml, helper platform/tooling/deploy/do-app.sh, workflow .github/workflows/deploy-digitalocean.yml; managed PostgreSQL wirebloom-pg (lon1), Spaces wirebloom-documents / wirebloom-archive, app wirebloom-bank created in project Wirebloom for bank.wirebloom.com. Code: @wirebloom/db/register trusts a database CA from DATABASE_CA_CERT (api, worker, migrate preload); storage accepts an S3-compatible endpoint in production behind STORAGE_ALLOW_CUSTOM_ENDPOINT=true with SSE-S3 (STORAGE_SSE). Docs: DEPLOYMENT §8, ENVIRONMENT §8, KNOWN_ISSUES N346–N352
  • 2026-09-25: MASTER_PLAN "Outstanding work and next phase" section (development complete; next phase testing and launch); task Status cells normalised so the dashboard counts them (notes moved to the Task cell); HANDOFF checkpoint refreshed.
  • Phase 11 / 12 review fix-forward (docs/reviews/PHASE11_12_REVIEW.md §5 and §7, commit 669cd97 on top of 0071977, 2026-09-25; full gate 118/118: api 1247, worker 485, db 238, web 848, mobile 370, providers 731, domain 647, portal 65, loadtest 32, mcp 33, crypto 38; contract 1.0.0-draft.14 unchanged in version, x-step-up regenerated additively; migration 0055_p1112_review_fixes, journal when 1791400000000): batch-rail import can no longer move a never-exported payment to processing / completed and the staff approval shows per-row effects (P1112-01); statements uploaded by someone other than the proposer, bound to the connection's settlement accounts, amount-less returns matched only when unambiguous (P1112-02); step-up coverage extended to customer limits, fees, pricing, billing, domains, branding, operators, provider-event replay and KYB decisions, with the 60-entry reviewed @NoStepUp list matched exactly by step-up-coverage.test.ts (P1112-03, P1112-14); audit bridges recorded by chain order at partition bounds with strict verification and the epoch-0 tail check (P1112-04, P1112-12; audit.chain_anchors.run_prev_hash / bridge, audit.record_partition_anchors, audit.bridged, audit.legacy_anchored); fair per-chain verification budget with audit.chain_verification_stale (P1112-05); default-partition moves keep dedupe keys (P1112-06); stale verified archives re-exported before a drop (platform.archive_current, P1112-07); archive bucket with Object Lock and delete denial (S3_BUCKET_ARCHIVE, P1112-08); operator-key fallback alert and KMS_OPERATOR_KEY_FALLBACK fail-closed switch, crypto-shred no_operator_key alert (P1112-09); outbox claim skips saturated queues' topics without breaking aggregate order (P1112-10); session-settings start-up check and the proxy SHOW / epoch-0 max(chain_seq) steps in DEPLOYMENT §4.14 (P1112-11, P1112-13); capacity-gate job failure ratio from pg-boss rows (P1112-29). Coord re-verification (§7): every Medium closed, 0 reopened, no test weakened; new P1112-62…-67 (2 Low, 4 Info) tracked as N324…N329 with the untracked Low follow-ups N330…N345. Verdicts: P11-T12 closed (Phase 11 complete, P11-T04 / P11-T05 now Met); P12-T01…P12-T07, P12-T09 Completed; P12-T08 / P12-T10 In Progress on the owner's staging capacity run (N323, Q-86) and staged-failure evidence (N316). Release candidate: no code blockers; operational checklist in RELEASE_READINESS.md §10. Next migration when > 1791400000000.
  • Phase 11 / 12 residual wave (commit 0071977, 2026-09-25; full gate 118/118: api 1242, worker 482, db 233, web 848, mobile 370, portal 65, providers 727, domain 647, loadtest 31, mcp 33, crypto 35; contract 1.0.0-draft.14 additive, 529 paths, 664 operations, 856 schemas; migrations 0050–0054; Coord review reviews/PHASE11_12_REVIEW.md running): security (P11-T01…P11-T11) approval-policy loosening under dual control with masked identifiers (0052), batch-rail imports under four eyes with statement reconciliation for credits and returns (0052), cross-operator session revocation and DSR pre-check parity, TLS verify-full enforced in production with pgaudit and log_parameter_max_length, capture_hold line validation, step-up on every /ops and /platform write (structural test), platform four-eyes on offboard (platform.operator_offboard_requests, 0054), staff rank guard, impersonation device revocation, CSP image origins and report throttling, redaction and placeholder-secret guards, anonymous and webhook IP buckets, OAuth consent page, email-change enumeration removed, supply-chain pinning, per-rule gitleaks, pnpm minimum release age, provenance and SBOM, envelope rewrap job, MCP per-client tokens with the read-only wb_mcp login (platform.mcp_clients, 0054); scale (P12-T01…P12-T09) per-operator audit chains with epoch-0 freeze and daily Merkle roll-up (0050, D-55), /v1/ready, per-job statement timeouts, stale-hold sweep, hot-queue partitioning, batched outbox pump and webhook ingestion, tenant fairness, API ceilings and processing ETA, notification and integrator fan-out batching, bulk submission sweeper, monthly range partitioning with a guarded copy-swap cut-over (0053, D-56), retention policy and 7-year Parquet archive with verified restore, read-replica and RDS Proxy routing, per-operator KMS keys with crypto-shredding, capacity gauges and alarms, trigram search indexes (0051), id look-ups, ETag on lists, web / mobile conditional polling, server pickers and bounded caches, capacity-test profiles with a COPY seeder and a scale-smoke gate. Residuals KNOWN_ISSUES N308…N323; decisions D-54…D-56.
  • Phase 10 close-out review fix-forward (docs/reviews/PHASE10_REVIEW.md §7, commit b07d896 on top of ae627b9, 2026-09-24; full gate 118/118: api 1185, worker 411, db 171, web 833, mobile 364, portal 65, providers 724, domain 640, sdk 33, contracts 37; contract 1.0.0-draft.14, 526 paths, 660 operations, 849 schemas): P10R-01 customer holds view shows compliance-gate and recall-case holds only as a neutral payment_review reservation (tipping-off); P10R-02 webhook registration routes, token rotation, connection deactivate and treasury exchange quotes run with no ambient transaction, the W1-11 exclusion becomes a lease (migration 0049, N193 resolved); P10R-03 provider-cost upserts take the cost-close lock and re-dated late lines leave post_close; P10R-04 open-banking revoke intents cleared / bounded to 1 h and no retirement on a page-capped IF list (alert); P10R-05 per-ref isolation of viban.details-sync, stuck order-intent alert, stale re-issue sweeper; P10R-06 one-click unsubscribe limited per token; P10R-07 step-up on sensitive operator settings and on the platform compliance floor (new capability platform.compliance, access reason), service step-up checks refuse callers without a session; P10R-08 developer registration without the in-transaction better-auth sign-up mail; W3-13 concurrent vendor-page link answers 409. KNOWN_ISSUES N298…N307, RELEASE_READINESS §3 rows 26–27.
  • Phase 10 wave 3 (commit 95c6ded, 2026-09-24; full gate 114/114: api 1153, worker 388, db 164, web 830, mobile 363, portal 65, providers 719, domain 640, sdk 33; contract 1.0.0-draft.13, 525 paths, 659 operations, 849 schemas, redocly 0 errors; Coord review in progress): P9-T07 / P9-T11 developer portal app platform/apps/portal generated from the contract (reference, guides, changelog, error catalogue, llms.txt, Markdown twins, public openapi.json, deprecation links; N85, N86 fixed; DEVELOPER_PORTAL.md) and the integration home with per-key request log and daily usage (migration 0046: iam.api_request_log, iam.api_key_usage_daily); P9-T08 / P9-T08b SDKs (TypeScript, Python; C#, Java, PHP, Go generated in CI from a hash manifest), webhook verification vectors, Postman collection and environments (SDKS.md), operator-level API keys and integrator webhooks with step-up (0042; P4 S-14 / Q-76 closed); P9-T09 / P9-T10 developer programme (invite or open registration, sandbox workspaces, abuse guard), 15 simulations incl. provider-backed, status page abstraction with Statuspage / Instatus adapters, incident comms, support requests (0043: platform.developer_*, platform.status_*, tenant.support_requests; SANDBOX.md, STATUS_PAGE.md, runbooks/INCIDENT_COMMS.md; N82 support half); P10-T04 / P10-T06 VIBAN pool lifecycle and orders, Banking Circle customer-details push (N58 part), account letters, partner-asset registry, IP-set change management, subscription and certificate alerts (0044; N163 fixed; INTEGRATIONS §12.3); P10-T05 / P10-T08 provider cost ingestion and monthly close, resolution packs, regulatory data packs (CSV / Parquet), IF open banking consents (0045; REGULATORY_PACKS.md, runbooks/BILLING_CLOSE.md). Wave 2 review fix-forward W2-01…W2-10, W2-12, W2-21, W2-27, W2-30, W2-53 (W2-48 partial; 0047 incoming hold guard and batched recipient screening). Direct-debit signedAt date bug fixed; capability naming convention (hyphens) with a seed / migration parity test; integrator event balance.details_changed. Documentation: MASTER_PLAN wave 3 rows In Review, KNOWN_ISSUES N220…N290 (wave 3 residuals and non-fixed W2 rows), OPEN_QUESTIONS Q-89, Q-90, RELEASE_READINESS §3 rows 23–25 and §7, DATABASE §17.
  • Phase 10 wave 2 (commit 74ce609, 2026-09-24; full gate 106/106: api 998, worker 338, db 148, web 759, mobile 360, providers 639, domain 640, kyc 34; contract 1.0.0-draft.12, 426 paths, 543 operations, 703 schemas, redocly 0 errors; Coord review in progress): P5-T09 Integrated Finance sync (migration 0039: providers.entity_links, providers.sync_status; list-based if.backfill, event mirroring of IF-born entities, nightly if.sync.drift with IF-wins statuses, conflict resolution, sync report and console tab); P10-T01 Banking Circle cases (0040: compliance.cases, case_events, case_document_requests, case_documents; RFI and inbound recall, CaseEvents ingestion plus polling, deadline reminders and escalation, four-eyes recall decisions, customer "Documents requested" on web and mobile); P10-T02 compliance-as-a-service (0041: counterparty screening provider in @wirebloom/kyc, pre-submission compliance gate with staff-only release and four-eyes threshold, monitoring rules with hold action and suppressions, EDD cases, MLRO designations and suspicion reports under restricted RLS, Integrated Finance external compliance mode); runbook runbooks/COMPLIANCE_OPERATIONS.md; webhook chain test; DECISIONS D-51. Residuals KNOWN_ISSUES N176…N192; non-fixed wave 1 review rows N193…N219.
  • Phase 10 wave 1 provider-platform parity (commit d4cea6c, 2026-09-24; full gate 106/106: api 932, worker 305, db 129, web 736, mobile 355, providers 554, domain 616; contract 1.0.0-draft.11, 377 paths, 486 operations; Fable review pending): P5-T07 activation checklist, activate / drain / deactivate endpoints, providers.provisioning_holds, console Activation card, runbooks/PROVIDER_SWITCH.md (migrations 0035, 0038); P5-T08 webhook registration from the console (Banking Circle subscription lifecycle, clienttest, key and token rotation with grace windows, IF registration pack and auto-confirmation, providers.webhook_endpoints, providers.webhook_rejections, dashboard; migration 0037); P5-T10 async report client, returns, multi-currency VIBAN, customer-details push planner, rails / serviceLevels / purpose-code catalogue, safeguarding mapping, UBO shape; P10-T03 individual and sole-trader customers, metadata attributes with filters, operator treasury payments and FX under four-eyes (payments.treasury_operations), rails target2 / ach / wire / eft, routing and purpose code catalogues, CNH (migration 0036). Env: BANKING_ENVIRONMENT, WEBHOOK_DR_BASE_URL, WEBHOOK_BC_MAX_AGE_HOURS, WEBHOOK_TOKEN_GRACE_HOURS, WEBHOOK_KEY_OVERLAP_HOURS, WEBHOOK_BC_FAILURE_EMAIL. Residuals KNOWN_ISSUES N160…N175.
  • Owner answers to Q-50…Q-88 recorded (2026-09-24; documentation only): OPEN_QUESTIONS rows marked answered with the differing answers noted; DECISIONS D-39…D-50 (BC contracting models, bidirectional IF sync, six SDKs, portal and registration toggle, per-operator status pages, compliance gate with WireBloom deciding holds, individuals in v1, cards and open banking consents, security, infrastructure, audit chains and keys, capacity) and D-32…D-36 Accepted; MASTER_PLAN row notes, new P10-T08 and P10-T09, P11-T05 decision, Phase 10 wave 1 (P5-T07, P5-T08, P5-T10, P10-T03) In Progress; PRD FR-INT-17, FR-PLAT-07, FR-PLAT-08, FR-ONB-07 in v1; INTEGRATIONS §10.6; COMPLIANCE §4.1 note; RELEASE_READINESS §3 row 18.
  • Scalability audit 2026-09 recorded (documentation only; reviews/SCALABILITY_AUDIT_2026-09.md: 11 High / 31 Medium / 8 Low): MASTER_PLAN.md Phase 12 — Scalability and capacity (P12-T01…P12-T10; T01–T04 In Progress with the High fixes), KNOWN_ISSUES N117…N155, OPEN_QUESTIONS Q-77…Q-88, ARCHITECTURE §10.1 capacity targets and scaling model, LOAD_TEST §9 capacity-test plan, RELEASE_READINESS §3 rows 15–16 and §9.
  • Security audit 2026-09 recorded (documentation only; reviews/SECURITY_AUDIT_2026-09.md: 0 Critical / 4 High / 8 Medium / 18 Low / 2 Info): MASTER_PLAN.md Phase 11 — Security hardening (P11-T01…P11-T12; T01, T02, T03, T06 In Progress), KNOWN_ISSUES N94…N116 for the findings not being fixed now, OPEN_QUESTIONS Q-67…Q-76 for the owner decisions, SECURITY.md §9 and RELEASE_READINESS §3 rows 12–14 and §8 (go-live ordering: P11-T01/T02 before real money, P11-T03/T06 before the first AWS deploy).
  • Parity programme consolidated into the scope documents (D-31 positioning; documentation only): research research/PARITY_INTEGRATED_FINANCE.md (IF parity 74 built / 19 partial / 7 missing), research/BANKING_CIRCLE_COVERAGE.md (BC coverage 34 / 20 / 23; recovered OpenAPI in research/banking-circle-oas/), research/PLATFORM_PARITY_SCOPE.md (SC-01…SC-61); MASTER_PLAN tasks P5-T07…P5-T11, P9-T07…P9-T11 (with P9-T08b) and new Phase 10 P10-T01…P10-T07 (P3-T15 and P5-T04 annotated); PRD FR-INT-09…16, FR-PLAT-05/06, FR-OPS-10/11, FR-ONB-07, FR-PAY-13, FR-REP-05 and a positioning note; INTEGRATIONS §10–§12 and §7 (BC answers from the OpenAPI, BC-24…BC-32, IF-25…IF-36); decisions D-32…D-36; questions Q-53…Q-66 (owner questions O-1…O-10 merged); known issues N85…N93; ARCHITECTURE §6 provider model; RELEASE_READINESS §7.
  • P6-T06 frontend review fixes (PHASE6_REVIEW_FRONTEND.md F-01…F-14, N81, S-05) and web draft.9 adoption: one safeReturnTo (apps/web/src/lib/url/return-to.ts: parses against a fixed origin, refuses control characters, backslashes, //, absolute URLs, auth paths and /api/) for the login machine, recipient wizard and every wizard close target (tab-bypass open redirect closed); nonce-based Content-Security-Policy per request from middleware.ts ('strict-dynamic', Sumsub only on /onboarding/*, Sentry ingest in connect-src, object-src 'none', frame-ancestors 'none', CSP_MODE=enforce|report-only, /api/csp-report counting stub) and HSTS in production builds (SECURITY.md §4.1); N81 root-caused in the browser and fixed (Suspense around the (app)/(auth) layout children, theme bootstrap moved to <body> with the nonce, hydration context on Sentry #418 events), known-issue entry removed after nine concurrent clean a11y sweeps; ops e2e asserts step-up positively and negatively (first release asks, cancel leaves the payment held, second action in the window does not ask, hold never asks); console guard also covers contexts a test creates (a11y phone contexts, checker contexts) and names the page of a page error; step-up prompts queue (F-04); GET /me 429 shows a rate-limit page with the wait and request id (F-05); one-time secrets reset from the mutation cache (F-06); fresh-key step-up retry removed (F-10); BFF turns upstream 3xx into 502 (F-11); floating feedback button desktop-only (F-12); branding host validated (WEB_BRANDING_HOSTS, F-13); mobile: production release check fails on empty certificate pins (F-08), KYC WebView refuses data: (F-09), recovery codes no longer shared as text (F-14); web on contract draft.9 (feedback endpoint, zod swap removed, staff directory, accounting providers, run-now runId, server canDecide, session-end reason on the login page).
  • Backend loose-ends pass (KNOWN_ISSUES N65, N79, N82, N62, N64, N59): contract 1.0.0-draft.9 (356 paths, 465 operations, 582 schemas; was draft.8 351 / 459 / 570); migration 0033 (tenant.feedback, warehouse.api_request_counts, drops payments.exchanges.confirmation_job_id / confirmation_document_id). Session look-ups served from the request cache with wb_cache invalidation (revoked sessions refused at once); 401 session-expired reason (idle / revoked / expired); product feedback POST /support/feedback and staff triage /ops/feedback (feedback.manage, 10 per hour per user); GET /ops/staff-directory (reports.view); KybCaseDetail.canDecide, PaymentException.canDecide; run now returns 202 { runId } and runs within seconds (reports.run); ?source=warehouse with dataAsOf on the three fact-backed reports; XLSX / PDF-summary report exports through the worker (reports.export); request metering into fact_usage.api_calls (API_METER_FLUSH_MS); accounting GET /integrations/accounting/providers, sync-log description / counterparty, comma-list status filter; @wirebloom/contracts/client without zod (headers.ts); alerts warehouse.drift and warehouse.stale with runbooks.
  • Compliance controls pass (COMPLIANCE.md §7, KNOWN_ISSUES N66–N70, N72, N73, N76–N78): migration 0032 (iam.terms_acceptances, tenant.dsr_requests, tenant.complaints, tenant.staff_approvals, tenant.compliance_reviews, ledger.safeguarding_reconciliations; customer retention / review / screening dates; company persons with ownership chains, control by other means, PEP declaration and source of wealth; providers.events.payload_purged_at) and contract 1.0.0-draft.8 (351 paths, 459 operations, 570 schemas). Neutral default statement and email footer with the operator's approved wording from settings.compliance.regulatoryFooter; GET/PUT /ops/compliance/settings holds every decision-dependent threshold with safe defaults; KYB approval requires completed KYC of every person and always records a risk rating; four-eyes (security.fourEyes) for KYB decisions, limit-exception and recipient approvals (/ops/four-eyes); staff closure decisions start the retention clock (7 y customer data, 5 y KYB documents); data-subject requests with 30-day deadlines, JSON + PDF data-map export and scheduled pseudonymisation; complaints (customer and staff routes, deadlines, notifications); terms acceptance recorded with a re-prompt and a setting-gated terms-outdated refusal of money movement; POST /audit-events/exports, GET /ops/audit/chain-status, audited report reads; worker jobs compliance.safeguarding-reconciliation (shortfall alert), compliance.screening-refresh (risk-based), compliance.periodic-review, retention.provider-requests, retention.provider-events, retention.customer-data (dry run until enabled); security anomaly alerts (MassDataExport, RepeatedDeniedActions, OutOfHoursRoleChange) and docs/runbooks/BREACH.md.
  • Cards module (P4-T13, FR-CARD-01/02, flag cards): CardIssuingProvider in @wirebloom/providers (src/cards: cardholders, virtual / physical cards, get / list, freeze / unfreeze / activate / terminate / replace / relink, controls, processor-hosted secure sessions for PAN and PIN, 3DS decisions, card transaction listing, neutral card events) with the Integrated Finance / Thredd adapter (recorded-style fixtures, inferred shapes flagged, never run against IF) and a deterministic sandbox issuer (simulated authorisations, settlements, reversals, refunds, 3DS challenges; the sandbox now reports the cards capability); the IF webhook parser adds data.cardEvent for the cards, card-transactions and card-transaction-authentications modules. API modules/cards: /cards CRUD and lifecycle (members limited to their own cards, ETag / If-Match, limits sent to the processor), /cards/{id}/details|pin secure sessions (holder only, step-up, no card data), /cards/{id}/activate|replace|terminate (terminate and replace now need step-up mfa.change), /cards/three-ds-challenges list / get / decision, /cards/{id}/transactions, operator /ops/cards list, transactions, compliance freeze / unfreeze; CardEventsWebhookHandler (binding pending in ProvidersCoreModule). Worker cards.apply-event (authorisation hold, settlement capture to card_collection with card_fx / card_atm fees, reversal, refund, 3DS challenge, status, wallet tokens), cards.sync, cards.3ds-timeout, cards.compliance-freeze. Migration 0031 (cardholders, card authentications, card and card-transaction columns, overdraft of card settlements from service contexts, pending card activity in the balance currency, compliance-hold trigger). Notifications card_status, card_transaction, card_3ds_challenge. Contract: Card (last4 / expiry nullable, frozenBy, statusReason, replacesCardId, controlsEnforcement, wallets), CardSecureSession (purpose, kind, token, sdk; url nullable), CardTransaction extensions, CardReplace, CardTerminate, CardOpsAction, ThreeDsChallenge; new operations activateCard, replaceCard, listThreeDsChallenges, getThreeDsChallenge, listOpsCards, listOpsCardTransactions, freezeOpsCard, unfreezeOpsCard. INTEGRATIONS §4.2 "Cards (Thredd)" and IF-20…IF-24.
  • Compliance requirements (P2-T07): new docs/COMPLIANCE.md (regulatory model options for Q-26 mapped to the ledger's provider_settlement / suspense / operator_ops accounts and the Banking Circle / Integrated Finance models; KYB data set against MLR 2017; UK GDPR roles, lawful bases, data map, retention reconciled with DATABASE.md §13, data-subject request design, processors, transfers, DPIA outline, 72-hour breach runbook; screening and monitoring policy; audit evidence and partner due-diligence checklist; complaints and T&C placeholders; gap register G-01…G-25). SECURITY.md §7 now summarises and links it; OPEN_QUESTIONS.md adds Q-38…Q-49. Documentation only; no code changed.
  • Performance tuning pass (KNOWN_ISSUES N61, LOAD_TEST.md §6.4): migration 0030 adds ledger.journal_lines.posted_at (entry posting time, trigger + back-fill) and keyset indexes for every activity source, splits ledger.customer_transactions into six source views (same columns), and adds ledger.customer_transactions_page (keyset + LIMIT inside every source; activity first page at 100k transactions 964 ms → 1 ms), ledger.customer_transactions_count, ledger.customer_activity_matches, pg_notify triggers for the outbox (wb_outbox) and for cache invalidation (wb_cache), and providers.events.processing_note. API: GET /transactions reads through the page function (cursor carries microseconds; contract unchanged), per-process request caches for tenant access / capabilities / operator settings with LISTEN-based invalidation (API_CACHE_TTL_MS), session.* rate limits in memory (RATE_LIMIT_SESSION_IN_MEMORY), DB_POOL_MAX, provider events no handler takes closed as ignored. Worker: per-queue concurrency / batch size for the hot payment-path queues (0.5 s poll, burst on full batches; WORKER_CONCURRENCY, WORKER_BATCH_SIZE, JOB_FAST_POLLING_INTERVAL_SECONDS), dedicated money pool (DB_MONEY_POOL_MAX), PGBOSS_POOL_MAX, queue groups @money / @background, continuous outbox pump (OUTBOX_PUMP_ENABLED, OUTBOX_PUMP_INTERVAL_MS; commit→dispatch p95 50.8 s → 37 ms). Local load profile PASS with every queued job drained in the window.
  • Staff mobile approvals, API side (P9-T03) and the scheduled-reports API (P9-T04 §10): contract 1.0.0-draft.7 (318 paths, 422 operations, 537 schemas). Staff step-up actions ops.payment_action (every staff payment action but the protective hold), ops.kyb_decision, ops.recipient_decision (operator-mode approve / decline; customer mode keeps recipient.create), ops.exception_resolve and ops.alert_resolve, declared (x-step-up) and enforced server-side, bound to the action, 5 minutes; Idempotency-Key required on POST /exceptions/{id}/resolve, optional on the recipient, KYB and alert decisions; OpsPayment.allowedActions from the payment machine and the caller's capabilities; canDecide on KYB review items, limit exceptions, monitoring alerts and operator-mode Recipient.approval; withTotal on /kyb/review-queue, /exceptions, /ops/monitoring-alerts, /ops/recipients; GET /ops/recipients/{id}, GET /ops/customers/{id} (standing, balances per currency, payments in 30 days, open issues), GET /ops/payments/{id}/provider-events (payments.ops, no payloads). Staff push: notification category operations (migration 0029, Android channel operations), templates ops_payment_attention, ops_kyb_submitted, ops_recipient_review, ops_exception_opened, ops_alert_opened (audience: 'staff', operator tenant, item ids, categoryId: 'approval') produced where payment exceptions, KYB submissions, operator-mode recipient reviews, limit exceptions and monitoring alerts open, to staff whose role holds the deciding capability. Staff idle timeout (30 min) enforced on mobile bearer requests and refresh (session and refresh tokens revoked); fixed the staff check behind the idle rule, which counted every user as staff once any staff membership existed (customers on the web got 30 instead of 60 minutes). /ops/report-schedules CRUD, run-now and run history on warehouse.report_schedules / report_runs (new capability reports.schedule for Op Owner / Admin / Finance). Platform console gaps (P6-T03): GET /platform/operators/{id}/usage (months or days) and GET /platform/usage from warehouse.fact_usage, GET /platform/health (worker heartbeats, job runs, outbox backlog, connections with breaker state, error rate and last reconciliation, webhook backlog, balance drifts), GET /platform/provider-catalogue (every adapter with form schemas, catalogue versions and capability matrix) with version POST / GET / PUT (ETag), GET /platform/audit-events (reason required, audited), Me.platformCapabilities, X-Access-Reason declared on /audit-events and /ops/dashboards/{dashboard}, plan search, Plan.operators and DELETE /platform/plans/{id} (refused while assigned); super_admin seeded with reports.view and audit.view. E2e: staff-approvals (11), staff-session (4), staff-report-schedules (3), platform-console (6).
  • Accounting integrations (P9-T02, FR-INT-08): new @wirebloom/accounting with the provider interface and Xero / QuickBooks Online adapters (OAuth 2.0 authorization code + PKCE, signed session-bound single-use state, KMS-enveloped tokens with refresh single-flight across processes, organisation/realm selection, chart of accounts, contact upsert, bank transactions / Purchase / Deposit with WB- markers, Idempotency-Key / requestid and marker lookup after unknown outcomes, per-organisation rate budgets and 429 handling), guarded HTTPS transport, recorded fixtures, scripted transport and provider simulators; API /v1/integrations/accounting (connect with step-up accounting_connection.change, callback, organisation selection, mappings validated against the chart, sync now, sync log, chart of accounts for the annotation category picker; customer.settings, sessions only); worker accounting.sync / accounting.sync-due / accounting.refresh-tokens with leases, keyset cursor with a settle window, per-leg backoff and dead-lettering; migration 0027 (tenant.accounting_connections, accounting_mappings, accounting_sync_log, RLS customer, posting trigger for automatic mode); contract additions (6 paths, 9 operations, 20 schemas, StepUpAction value accounting_connection.change); INTEGRATIONS.md §9 and runbooks/ACCOUNTING_INTEGRATIONS.md. Not yet run against real Xero/QuickBooks developer accounts (INTEGRATIONS.md §9.7).
  • Reporting warehouse, scheduled reports and MCP operational tools (P9-T04, RULES §15/§51): migration 0028 adds schema warehouse (staff RLS; reference dims readable, watermarks service-only) with dim_date/currency/rail/operator/customer/provider, fact_transactions (per journal line), fact_payments (lifecycle timings, SLA), fact_fees, fact_onboarding, fact_provider_calls, fact_usage (+ per currency), watermarks, rechecks, report_schedules, report_runs; new @wirebloom/warehouse (incremental idempotent loaders with UUIDv7 / updated_at keysets and a 15-minute overlap, nightly facts-vs-ledger re-check with backfill, retention, the operator report catalogue with the API's OLTP SQL plus warehouse-backed fee-income / provider-settlement / onboarding-funnel, schedule periods and cron validation, dependency-free PDF summary); worker jobs warehouse.refresh (10 min), warehouse.recheck (03:15, critical alert warehouse.drift) and reports.scheduled (5 min: CSV / XLSX / PDF document, report_ready to staff with reports.view, audit report.scheduled_generated); notification template report_ready; new app @wirebloom/mcp (stdio and stateless streamable HTTP with a static bearer token, loopback by default, distroless Dockerfile): eight read-only tools (platform.health, operator.summary, customer.lookup, payment.trace, reconciliation.status, queue.status, report.run, audit.search) with a mandatory audited reason (mcp.tool_call), read-only transactions, PII masking and row / byte limits. Docs: WAREHOUSE.md (model, refresh, metric definitions, API proposal), MCP_TOOLS.md.
  • P6-T01 API mismatches: contract 1.0.0-draft.6 (302 paths, 399 operations, 502 schemas): GET /ops/journal items with lines; idempotency releases the key on 401/403/429 (retry after step-up with the same key succeeds); typed TransactionExportFilter; GET /documents status/date/search filters; scheduled payments withTotal and template.recipientName; Recipient.approval with canDecide, Approval.received, GET /payments/{id}/approvals, primary-owner transfer list/get; Team.updatedAt / IntegratorWebhook.updatedAt; defaulted fields optional in the generated client types (defaultNonNullable: false); Rail bacs end to end with migration 0026 (ALTER TYPE platform.rail ADD VALUE IF NOT EXISTS 'bacs'); GET /me 429 test; Expo pushes carry the Android channelId per category and categoryId: 'approval' on approval requests; ENVIRONMENT.md rows for OAUTH_MOBILE_REDIRECT_URIS (https /auth/callback in staging/production) and MOBILE_MINIMUM_APP_VERSION.
  • API gaps for the operator console, customer screens and mobile (P6-T06 preparation): contract 1.0.0-draft.4 (300 paths, 396 operations, 500 schemas) with typed report rows, dashboard widgets, provider connection config/credentials (JSON Schema generated from the adapters' zod), monitoring params, notification data and WebAuthn options; staff reads of balances, payment events and approvals across the operator; /ops/customers, the customer communication log, /ops/staff and custom staff roles; operator GET /provider-catalogue; fee catalogue item GET and customer-readable fee charges; display names on exception queues and KYB; mobile device binding (deviceId, expiresIn), refresh-token-reused, RFC 7009 revocation and Health.minimumAppVersion; GET /notifications/{id}, Device.current, exchange confirmation as PDF, webhook.test; e2e suites money-gaps, ops-gaps, identity-gaps, notifications-gaps. No migration.
  • Partner adapters wired into the platform (P5-T06): API and worker register Banking Circle with a database VIBAN pool (providers.viban_pool / viban_orders, SELECT … FOR UPDATE SKIP LOCKED, per-holder advisory lock) and the batch rail with a database batch store (providers.batch_files / batch_rows; late appends move to the next batch, rendered payments cannot be cancelled), both in @wirebloom/db stores; webhook receiver sets x-wirebloom-source-ip from the trusted client address (client values stripped) and accepts Banking Circle's application/octet-stream bodies; onboarding sends Integrated Finance the registered address, relations, registration and risk, pushes Banking Circle Pobo/Cobo VIBAN customer details, opens provider accounts per routed currency (customer_provider_refs.accounts, ledger.provider_accounts with the IF account model) and ledger provisioning links the balances; worker maps providerStatus through the partner tables and raises the new payment exception provider_attention (also for rolled IF conversions), polls exception payments (M-09) and matches direct debits on our end-to-end id (M-02); Integrated Finance recoverWebhooks; HTTP client text/XML bodies and safe redirects; neutral providerStatus / attention / source fields; batch rail console /v1/ops/batch-rail/* (status, files, export, import with step-up batch_rail.import); POST /provider-connections/{id}/test {"dryRun": true}; contract 1.0.0-draft.5 (299 paths, 395 operations); migration 0024; INTEGRATIONS.md v2 final (§4.4 wiring, §7 consolidated partner questions BC/IF/BR, §8 go-live checklists), runbooks completed (BANKING_CIRCLE.md, INTEGRATED_FINANCE.md, new BATCH_RAIL.md), DATABASE.md 0023/0024, OPEN_QUESTIONS Q-25 status and Q-31…Q-33; tests: API e2e providers-adapters (9) and batch-rail (3), worker integration (onboarding accounts, attention, M-09 poll, DD end-to-end id, FX roll), unit (source IP, mappingOf, HTTP client redirects/text, IF recovery).
  • Phase 4 consolidation (P4-T15): cross-currency payments wired end to end (PaymentsService binds the exchange, routes submission through payment.exchange_required, releases on cancel/reject, dry-run FX fields; e2e create → exchange → submit with the worker engines in-process; closes N49); webhook chain Sumsub → Resend → Exchange → DirectDebit → PaymentEvents → queue; PaymentsModule exports OpsPaymentsService and TransfersService; payments, bulk batches and recipients open approvals through ApprovalsService.open (approvals-core.module.ts), worker-created approvals fill the 0017 columns, payment views count approval_decisions; customer roles gain reports.export (all) and audit.view (owners/admins); recipients approvalMode fallback operator; nine notification templates (exchange_status, scheduled_payment_failed, scheduled_payment_inactive, mandate_status, collection_status, collection_pre_notification, fee_funds_required, recipient_verification, webhook_paused) with the reusing call sites switched; contract 1.0.0-draft.3 (289 paths, 379 operations) with the whole P4 backlog, no hidden routes left, not-implemented (501) mapped from adapters; @wirebloom/integrations-webhooks (N50 guard/signing/transport); storage XLSX; telemetry hook timeout 60 s; DATABASE.md 0011–0022, worker job catalogue, outbox topic table. Money review fixes: no payment without a hold is ever sent and hold-less captures/returns raise provider_conflict (M-01), scheduled cross-currency payments converted on their date with a partner re-quote (M-04), direct-debit polling matches only the collection's own partner reference and never re-credits a consumed transaction (M-02), in-flight conversions cannot be cancelled and a late partner completion is booked (M-03), ledger.integrity checks subject invariants (M-07); tenancy review: drawdown events matched on the event's operator and connection (T-02), tenant jobs scoped to the envelope operator (T-03).
  • Phase 3 consolidation (P3-T16): contract 1.0.0-draft.2 (248 paths, 332 operations) with the P3-T05/T06/T12/T13/T14 backlog: GET /documents, POST /documents JSON upload intent + DocumentWithUpload, POST /documents/{id}/complete, Document.status, notification Web Push key/test-send/unsubscribe routes, emailDigest, POST /me/email-change/confirm, Me.impersonation.id, LoginResult.passkeyOptions, optional If-Match for items without a GET, capability rules (primary-owner confirm self.profile, customer.decide, staff invitations staff.manage), problem type domain-not-approved; API serves them (no hidden routes). Resend delivery webhooks applied through WEBHOOK_DISPATCHER; branded identity emails via createEmailSender; MFA secrets on @wirebloom/crypto with KMS and a secrets.rotate-mfa job; identity.purge-expired job; wb_pgboss role/schema and weak-password refusal in the migrate task; RDS CA bundle and telemetry preload in the api/worker images; auth/provider/breaker/webhook metrics, Sentry capture of 5xx, web withSentryConfig; shared vitest presets everywhere and measured coverage floors; e2e smoke signs in. P4-T04/P4-T05 follow-ups: contract adds operator domains (6 routes), public logo, customer standing, settings export, plan/flag GETs, branding/settings fields, customer-mode recipient approve/reject, RecipientRequirements.alternatives, approvalMode: none with verification settings (contract now 257 paths, 345 operations); login step 1 returns operator branding; emailFromName in the From header; recipient-specific notification templates; @wirebloom/db/domains (domain verifier, alert triggers) and seedOperatorBilling (used by operator provisioning); worker jobs tenancy.domains.verify and balances.watch-low (stub).
  • Technical audit of the legacy portal (review/WireBloom_Portal_Technical_Audit_2026-09-23.pdf) and requirements-compliance presentation.
  • Documentation set v2: SCOPE_OF_WORK, PRD, ARCHITECTURE, DESIGN_SYSTEM, DECISIONS (D-00…D-27), OPEN_QUESTIONS, MASTER_PLAN, HANDOFF, DATABASE, SECURITY, TESTING, DEPLOYMENT, README, PROJECT_PLAN.html generator.
  • Research: Equals Money product and UX review; Integrated Finance API review; Banking Circle Connect API review; INTEGRATIONS.md (provider interface mapping, capability matrix, per-tenant configuration, partner questions); brand assets.
  • OpenAPI 3.1 contract docs/api/openapi.yaml (243 paths, 325 operations, 357 schemas; /public/currencies, Health with checks, generic 405/409/415 problem types) and docs/api/API.md; generator under docs/_tools/openapi-gen/.
  • DATABASE.md: tables for limits and exceptions, integrator webhooks and deliveries, manual postings, customer notes, low-balance alerts, closure requests, jobs, API idempotency keys; funds requests gain a source account.
  • PRD Appendix A: read capabilities, annotations, onboarding, impersonation, limits, reconciliation, webhooks and the platform.* split.
  • Frontend review fixes (P6-T06): shared safeReturnTo (open-redirect variant closed), Content-Security-Policy with per-request nonces and strict-dynamic plus HSTS and a report endpoint (S-05 resolved), e2e step-up assertions replacing the optional seam, hydration issue N81 root-caused (page segment streamed into a Suspense-less <main>) and fixed with layout Suspense boundaries (9/9 concurrent sweeps clean), queued step-up prompts, rate-limited page for GET /me 429, one-time secrets cleared from the cache, BFF 3xx handling, branding host allow-list, mobile release-check pin guard and WebView data: refusal; draft.9 adopted in web; 688 web tests, e2e 60 passing with 0 known-issue annotations.
  • Coord: final full gate 106/106 (2026-09-24), prettier clean, schema no diff, workflows lint clean.
  • Mobile draft.9 adoption: session-end reasons (idle/revoked/expired with refresh-and-retry on expired), KybCaseDetail.canDecide, payment-exception queue kind with deep links and resolutions, feedback sheet under More; 340 mobile tests.
  • Backend loose-ends pass: session cache wired with trigger invalidation (N65), feedback API (POST /support/feedback, ops triage, feedback.manage), canDecide on KYB detail and payment exceptions, staff directory for pickers, run-now returns a run id via a reports.run job, accounting providers list and log fields, 401 session-expired reason, zod-free contracts client entry, warehouse-backed report source with dataAsOf, XLSX/PDF report exports as jobs, API request metering into fact_usage, warehouse.stale alert, exchange confirmation columns dropped; contract 1.0.0-draft.9 (356 paths, 465 operations); migration 0033; full gate 106/106 (api 863 tests).
  • Accessibility and performance pass (P6-T05) with the remaining web screens: focus return and scroll-region fixes in @wirebloom/ui, 44 px touch targets at coarse pointers, sign-in focus management, phone sweep of 70 routes (0 axe violations, 0 horizontal scroll), docs/ACCESSIBILITY.md; Lighthouse CI config and route bundle budgets (Home first-load JS 510 → 308 kB gz, desktop LCP 977 → 838 ms), lazy palette/WebAuthn/drawers, zod kept out of the browser, docs/PERFORMANCE_WEB.md; accounting integration settings and callback, annotation category picker; scheduled reports page and report_ready deep link; platform usage screens; feedback widget and help centre (15 articles); four-eyes page and 202 StaffApproval handling; 632 web tests, e2e 49 passing.
  • Coord: full monorepo gate 106/106 (2026-09-24); CI gains the bundle-budget step and a gated Lighthouse job; migration journal renumbered by timestamp.
  • Compliance controls pass (N66–N78): operator compliance settings (regulatory footer/model, KYB verification and risk defaults, cadences, retention, terms, four-eyes), daily safeguarding reconciliation with shortfall alerts and sign-off, KYB approval requiring completed person KYC and a risk rating, company-kind persons with ownership chains and PEP/source-of-wealth fields, risk-based re-screening and periodic reviews, closure decisions setting the retention clock, retention purges (provider requests/events, customer data pseudonymisation in dry-run), data-subject requests with export/erasure jobs, complaints with SLA dates, four-eyes staff approvals with maker≠checker, terms acceptance records and re-prompt, audit-event export and chain status, breach runbook and three anomaly alerts; migration 0032; contract 1.0.0-draft.8 (351 paths, 459 operations); 19 e2e + worker integration tests.
  • Cards module (P4-T13, flag cards): CardIssuingProvider with an Integrated Finance/Thredd adapter (35 fixtures, scripted transport) and a deterministic sandbox, cards lifecycle (create/activate/freeze/unfreeze/terminate/replace), controls, processor-hosted secure sessions for PIN/PAN, 3DS challenges and decisions, card transactions with authorisation holds, settlement capture to card_collection, FX/ATM fees, refunds, operator oversight and freeze-on-hold; worker cards.sync/cards.3ds-timeout; card webhook handler bound into the dispatcher chain; migration 0031 (cardholders, card_authentications, card columns, post_entry overdraft rule for card presentments); 16 e2e + 9 worker tests; INTEGRATIONS cards subsection with IF-20…IF-24.
  • Mobile draft.7 adoption: staff decisions on the real ops.* step-up actions with allowedActions/canDecide, single-call staff reads, withTotal badges, staff push and report_ready document screen, cross-tenant notification taps via account switch, deviceId/expiresIn/refresh-token-reused/revoke handling, recipient and payment approval views, current-device marking; 312 mobile tests.
  • Performance pass (N61): activity read model split into six keyset-paged source views with customer_transactions_page, journal_lines.posted_at and keyset indexes (first page 964 ms → 1 ms at 100k), outbox LISTEN/NOTIFY pump (commit→dispatch p95 51 s → 37 ms), hot-queue concurrency/batching with dedicated money pools, per-request TTL caches with trigger-driven invalidation, in-memory session rate-limit buckets, DB_POOL_MAX/DB_MONEY_POOL_MAX, unhandled provider events closed as ignored; migration 0030; before/after in LOAD_TEST §6.4.
  • Staff API pass (P9-T03/P9-T04/P6-T03 API side): contract 1.0.0-draft.7 (318 paths, 422 operations, 537 schemas): staff step-up actions enforced on decision routes, idempotency on exception resolution, staff reads (/ops/customers/{id}, /ops/recipients/{id}, per-payment provider events), allowedActions/canDecide/withTotal on queues, operations push category with five ops_* staff templates wired to producers (migration 0029), 30-minute idle rule for staff bearer sessions (and a fix that gave customers the staff timeout), /ops/report-schedules with capability reports.schedule, platform health/usage/audit/provider catalogue versions/plan delete, Me.platformCapabilities; 809 api tests.
  • Platform console and web leftovers (P6-T03): platform operators/plans/flags/providers/health/audit screens with an access-reason dialog, customer onboarding wizard with hardened Sumsub WebSDK embedding and sandbox test panel, direct-debit mandate wizard and detail, batch-rail ops screen, approvals oversight on GET /approvals, translatable @wirebloom/ui labels via UiLabelsProvider, app-link association routes for mobile; 543 web tests, 36 e2e flows green; contract draft.6 fallout fixed.
  • Accounting integrations (P9-T02): @wirebloom/accounting with Xero and QuickBooks Online adapters (OAuth2 + PKCE, signed one-time state bound to session/customer, KMS-enveloped tokens with cross-process single-flight refresh, contacts upsert, bank transactions/purchases with deterministic idempotency markers, rate-limit pauses, revoke), API /integrations/accounting/* with step-up, worker sync engine over journal legs with retries/dead-letter and an automatic mode driven by a journal trigger; migration 0027; 62 tests; INTEGRATIONS §9 and docs/runbooks/ACCOUNTING_INTEGRATIONS.md.
  • Reporting warehouse and MCP tools (P9-T04): warehouse schema (date/currency/rail/operator/customer/provider dimensions; transaction, payment lifecycle, fee, onboarding, provider-call and usage facts) refreshed incrementally every 10 minutes with watermarks and a nightly recheck against the journal (warehouse.drift alert), scheduled reports (report_schedules/report_runs, CSV/XLSX/PDF summary, report_ready notification), warehouse-backed fee-income/provider-settlement/onboarding-funnel with OLTP parity tests; apps/mcp read-only MCP server (8 tools, mandatory audited reason, masking, limits, stdio + localhost streamable HTTP with bearer token); migration 0028; docs/WAREHOUSE.md, docs/MCP_TOOLS.md.
  • Load test and drills (P8-T02 prep): @wirebloom/loadtest k6 scenarios (login/TOTP, reads with cursors, payment create with idempotency, approvals with step-up, signed webhook bursts, API keys, rate-limit probe, quotes) with NFR thresholds, seeding, DB probes and Markdown reports; local 90 s run passed (62.8 req/s, 0 errors, trial balance 0 after load); restore drill script with local PASS (RTO 11 s on a 207k-line ledger) and the AWS PITR procedure; failover runbook; manual loadtest.yml workflow; docs/LOAD_TEST.md.
  • Legacy import tool (P8-T06, D-21): @wirebloom/legacy-import CLI with a snapshot reader (JSONL manifest or pg_dump --data-only), deterministic mapping plan and PII-free report, customers → operator customers with queued invitations and KYB snapshots, beneficiaries → encrypted recipients with duplicate collapse, balances → opening journal entries against Legacy migration <CCY> suspense accounts (reconciled per currency), history → monthly statement PDFs, idempotent resumable apply with audit and compensating rollback, synthetic generator with the audit quirks; 76 tests; docs/LEGACY_IMPORT.md.
  • Web API mismatches (P6-T01 follow-up): contract 1.0.0-draft.6 (302 paths, 399 operations, 502 schemas): journal items carry lines, idempotency keys released on 401/403/429 so a step-up retry succeeds, typed TransactionExportFilter, document filters, scheduled withTotal, Recipient.approval with canDecide, Approval.received, ScheduledPaymentTemplate.recipientName, GET /payments/{id}/approvals, primary-owner transfer reads, Team/IntegratorWebhook.updatedAt, defaulted fields optional in generated types, bacs rail end to end (migration 0026), push channelId/categoryId per category.
  • Staff mobile approvals (P9-T03): staff mode in the Expo app (mode detection from operator memberships, protected route sets, 30-minute idle sign-out persisted across cold starts), unified queue over payment actions, recipient approvals, KYB decisions, limit exceptions and monitoring alerts with step-up before every decision and per-decision idempotency, read-only customer summaries, staff push routing and operations channel, Maestro staff flow; 281 mobile tests.
  • Customer web screens (P6-T01): Home, Balances with account details, Transactions (filters, saved filters, column chooser, export jobs, drawer with annotations/labels/attachments/confirmation), Statements, Payments hub with send/add money/exchange/transfer/bulk wizards, scheduled payments with RRULE builder, forward contracts and held rates, direct debits (collections), cards placeholder, limits, Recipients with requirements-driven wizard and CoP override, Requests (approvals inbox, funds requests), People and teams, all Account settings sections incl. API keys/webhooks and approval-policy tiers, notifications centre; capability/flag-aware navigation; per-area message catalogues; 432 web tests, 5 enabled Playwright flows + axe sweep; passkey step-2 fix (N55).
  • Mobile release pipeline (P7-T04): EAS profiles with fingerprint runtime versions and update channels, release-check script, Sentry (errors only, parity scrubber), encrypted MMKV cache with Expo Go fallback, Keychain purge on reinstall, OS-level certificate pinning plugin (pins supplied by owner), screenshot protection and clipboard auto-clear, device-integrity advisory, Android notification channels and approval actions, forced-update gate, .github/workflows/mobile.yml, docs/MOBILE_RELEASE.md; 241 mobile tests.
  • Coord: full monorepo gate 86/86 tasks, prettier clean, schema no diff (2026-09-23 late evening).
  • Console and mobile API gaps (P6 prep): contract 1.0.0-draft.4 (300 paths, 396 operations, 500 schemas) with typed reports, dashboards, provider connection config/credentials per adapter (JSON Schema from zod), monitoring params and operator settings; staff reads on balances/payment events/approvals; /ops/customers with issue counts, communication log, /ops/staff and /ops/staff-roles with subset validation, operator-readable provider catalogue, fee catalogue item ETags, GET /notifications/{id}, RFC 7009 revoke, mobile deviceId/expiresIn/refresh-token-reused, Health.minimumAppVersion, exchange confirmation as PDF, data.tenantId on notifications, Device.current; 747 api tests.
  • Mobile features (P7-T02 + P7-T03 client side): TanStack Query with encrypted-session-gated AsyncStorage persistence and tenant-scoped keys, all customer screens (balances/account details, activity with filters/exports/statements, transaction detail with notes/attachments/confirmation share, recipients wizard with on-device bank validation and CoP display, send/add money/exchange/transfer/scheduled, approvals and funds requests, notifications centre/preferences/devices, settings incl. biometric lock and sessions, onboarding status with Sumsub WebSDK in a hardened WebView and optional MobileSDK launcher), push handling with safe deep links and badge, Maestro flows written; 192 tests; typed routes checked in CI.
  • Reconciliation (P5-T04): per-connection runs with persisted cursors, every booking matched/suspended/excepted exactly once (property-tested), missed webhooks applied through the existing engines, balance comparison with in-flight tolerance and drift alerts feeding ledger.integrity, exception resolutions (allocate, match, write-off with dual control, ignore) with step-up and idempotency, failed-webhook replay and adapter recovery (recoverWebhooks?), migration 0023 (reconciliation_balances, reconciliation_cursors, new exception kinds); 14 worker + 7 api tests; docs/runbooks/RECONCILIATION.md; review items M-14 resolved, M-09 mostly resolved, M-05/M-06 mitigated.
  • Operator console (P6-T02) and provider connection console (P5-T03): 36 routes: dashboards, customers with nine tabs (standing, people/impersonation, balances, payments, limits editor, pricing, notes, documents), KYB review queue with decisions and screening, payment operations with actions and exceptions (payment, limit, monitoring), recipient approvals, journal with manual postings (dual control, step-up) and reconciliation views, pricing (typed rule editor with preview, customer pricing, billing runs, fee charges, settlements, ERP export), reports runner, audit log, staff, operator settings, white-label branding with live preview and domains, provider connections (schema-driven wizard, write-only credentials, test/capabilities, routing rules validated against capabilities, events replay, KYC connections); 13 vitest suites + 4 axe suites, 7 Playwright ops flows, 5 screenshots.
  • Banking Circle adapter (P5-T01) and batch CSV adapter (P5-T05): full BankingProvider for Banking Circle Connect from the crawled documentation (mTLS/Basic token cache, master accounts and VIBAN pool with customer-details modification guard, credit-transfer instructions with idempotency replay handling, status mapping with attention flags, cancel/recall, RFQ/held-rate FX with cross-process quote ids, SDD direct debits, AES-256-GCM webhook verification, camt.053 + bookings reconcile) with 40 HTTP fixtures and a no-network scripted transport; operator-run batch rail with export/import formats; registry now lists sandbox, banking-circle, integrated-finance, batch-csv; providers package 308 tests; docs/runbooks/BANKING_CIRCLE.md; INTEGRATIONS §4.1/§4.3 rewritten.
  • Integrated Finance adapter (P5-T02): full BankingProvider against the public documentation with 61 recorded-style fixtures and a scripted transport: RS256 assertion auth with single-flight token cache, clients and accounts, deposit info, statements with organisation-side filtering, beneficiaries/requirements/CoP/VoP, outgoing transfers with idempotency and compliance checks, exchange quotes/transactions, generic transactions, Ed25519 webhook verification against the published key, failed-webhook recovery, reconcile; 136 tests; docs/runbooks/INTEGRATED_FINANCE.md; INTEGRATIONS §4.2 rewritten.
  • Mobile scaffold (P7-T01): expo-router auth and app stacks, native two-step login with TOTP/recovery and first-login enrolment, browser PKCE sign-in with https app links, in-memory access token and biometric-gated refresh token in secure storage with single-flight rotation, step-up sheet with retry, operator branding with contrast parity to the web, version gate, push device registration, brand icon/splash, privacy manifest placeholders; 99 tests; expo export for both platforms and expo-doctor clean.
  • Security fixes (P4-T15): impersonation bound to the target membership (S-01), explicit money-movement classification under impersonation (T-01), client-IP trust by CIDR plus signed BFF viewer header and account-keyed per-IP buckets (S-02), URL token masking in logs/spans (S-03, S-10), fastify override to 5.12.5 with a blocking audit gate (S-04, S-30), MFA-reset role guard (S-06), per-account MFA failure lock (S-07), CSV formula guard (S-08), __Host- cookie prefix (S-17), worker log redaction (S-25/26); SECURITY.md §1.4 and ENVIRONMENT.md updated.
  • Activity and reports (P4-T12): unified transactions read model as a security_invoker SQL view with one filter function shared by list, totals and exports, keyset paging, detail timeline, annotations/labels/attachments, async CSV/XLSX exports (dependency-free XLSX writer) as jobs, monthly statement job and CSV/XLSX statements, cached branded confirmations, /jobs/{id}, staff reports catalogue with CSV exports, five dashboards with 60 s cache, scoped audit log; migration 0021 (transaction_metadata, confirmations, view and function); 17 e2e + 12 worker tests.
  • Coord: drizzle snapshot regenerated as 0022 after the six-migration wave; generate reports no schema changes.
  • Public API (P4-T14): API keys (once-shown secrets, scopes, expiry, IP allow-list, rotation with grace, revoke, per-customer limits), integrator webhooks (HTTPS + SSRF guard with resolve-time checks and pinned connections, KMS-wrapped secrets with 24 h dual signing, contract retry schedule, pause after 20 failures, disable on exhaustion, deliveries and redelivery), trigger-based event capture into integrator.* outbox topics with fan-out, sandbox simulations through the real provider-event pipeline, wb_test_ keys gated per environment; sample integration client under platform/examples; docs/api/INTEGRATORS.md; migration 0022; 22 e2e + 8 worker tests.
  • Exchange (P4-T08): quotes with operator FX spread and domain rounding sides, execution and settlement postings in the worker (fx_clearing residue as FX result), confirmation render, held rates and forward contracts (deposit sub-balance, drawdowns, maturity, margin calls) gated by capability and the fx.forwards flag, add money by conversion, cross-currency payment quote binding in preparation; migration 0018 (exchange_drawdowns); 26 e2e + 13 worker tests.
  • Scheduled payments and direct debits (P4-T09): standing orders on a date-based RRULE subset with business-day adjustment and time zones, hourly occurrence generation with idempotent occurrence rows, worker instantiation mirroring payment preparation, 3-failure inactivation, operator pre-approval setting; mandates (encrypted debtor identifiers, references, client-managed), collections with scheme lead times, submission, event/poll status, settlement postings, return windows and reversals, mandate expiry; migration 0019 (scheduled_occurrences); 28 e2e + 14 worker tests.
  • Limits and monitoring (P4-T11): effective-dated limit sets (operator defaults, customer overrides, tombstones) with contract field mapping, usage rollups in the operator timezone from a payments trigger + outbox (limit_usage, closes N46), limit exception queue resolved through the payments ops service, five monitoring rules with alerts and KYC re-screen hooks; migration 0020 (recreates limits_scope_unique and the bound check: accepted pre-launch); 14 e2e + 7 worker tests.
  • Approvals and funds requests (P4-T07): /approvals inbox for payments, bulk batches, recipients, funds requests and primary-owner transfers with tier snapshots, approval_decisions rows folded through the domain (maker refused with dual-control, role/team eligibility), step-up on approve, expiry and reminder worker jobs, notifications to qualified approvers; funds requests with policy tiers, immediate fulfilment by internal transfer, cancel and expiry; migration 0017; 20 e2e + 4 worker tests.
  • Payments (P4-T06): creation with recipient/currency/limit/approval/routing/cut-off checks and hold placement, conditional step-ups, idempotent replay, dry-run with fee quotes, drafts, submit/cancel, transfers, add money (same currency), bulk CSV (streaming validation, per-row results, batch approval), operator queue with execute/hold/release/recall actions, exceptions queue (stuck, limit tolerance/queue, provider error, unmatched incoming, return unmatched), incoming credits with suspense allocation, returns linked to originals; worker payments engine (submit, poll, scheduled release, stuck detection, bulk validate) calling the ledger functions and fees; provider event hand-off through the webhook chain; domain payment references and CSV parser; migration 0015 (payment_exceptions, bulk_upload_rows, manual-posting columns); 23 e2e + 18 worker integration tests.
  • Coord: payment-events handler bound into the webhook chain; providers e2e keeps a queue-only dispatcher via Nest overrides; billing test customer-number flake fixed.
  • Fees and billing (P4-T10): fee catalogue, effective-dated pricing templates with typed rules (fixed, percentage min/max, tiered, per-rail, FX bps, monthly, card custom), customer pricing assignments and overrides, FeeQuoteProvider for payments, fee charges posted through the ledger with waive/refund reversals, monthly billing runs with pro-rating and funds-required items settled on incoming funds, fee settlement to operator ops, fee-income and ERP CSV exports as documents; worker jobs billing.monthly-run, billing.settle-pending, billing.settlement; migration 0016.
  • Ledger service (P4-T02): PostingService (post, holds with partial capture, release, reverse, replay-safe), LedgerService (race-safe main balances, pots and pot transfers, operator singleton accounts, provider mirrors, account status machine, currency enablement), BalanceQueryService (balances, holds, journal with running balance, account details per rail, statement data), accounts routes per contract, manual postings with dual control, read-only reconciliation views, worker jobs ledger.integrity and ledger.provision-customer (consumes customer.approved), migration 0012 (ledger.integrity_runs, provider reported balances, statement jobs); SQL/in-memory template parity test and the 100-parallel-payments acceptance test.
  • Coord: Sumsub webhook handler bound in front of the Resend handler; test worlds gained a provider-override hook; full gate 76/76.
  • Onboarding and KYB (P4-T03): @wirebloom/kyc (KycProvider interface, Sumsub adapter with HMAC signing, applicant lifecycle, review mapping, screening flags; deterministic sandbox adapter), KYB wizard per contract (/customers/{id}/kyb/*), submission snapshots, RFI round-trips, operator review queue with decisions and screening resolution, provider recommendations with optional auto-approve, Sumsub webhook handler, worker jobs onboarding.provision-customer (IF/sandbox createCustomer, Banking Circle VIBAN details with the 2-modifications guard) and onboarding.screening-refresh, outbox event customer.approved; migration 0014; 16 e2e + 6 integration tests.
  • Phase 3 consolidation (P3-T16): contract 1.0.0-draft.2 (257 paths, 345 operations) with every backlog item applied, branded identity emails, impersonation.id, email-change confirm, Resend and Sumsub handlers bound to the webhook dispatcher, wb_pgboss role and weak-password refusal in migrate, RDS CA bundle in images, telemetry hooks, MFA secrets on @wirebloom/crypto with dual read and secrets.rotate-mfa, jobs identity.purge-expired, tenancy.domains.verify, balances.watch-low, vitest presets and measured coverage floors, signed-in e2e smoke.
  • Recipients (P4-T05): requirements per currency/country from the routed connection with a static fallback table and alternatives, envelope-encrypted identifiers with per-operator HMAC duplicate detection, masked listings with audited full reads, CoP/VoP verification with stored outcomes and overrides, approval modes customer/operator/none through the domain machine and tenant.approvals, provider beneficiary refs per connection, requireApproved and audited snapshotForPayment for payments; migration 0013; 20 e2e tests.
  • Tenancy administration (P4-T04): operator branding with server-side WCAG validation and logo documents, GET /public/branding by verified host (cache, ETag, never 404), web domains verified by DNS TXT, sender domains through a Resend-backed provider, operator settings in contract shape, platform administration (operators status machine, plans, flags) with access reason and audit, customer standing changes by staff, settings export, keyset paging, alert trigger cooldowns; migration 0011 (platform.operator_domains); seeds mapped to contract branding keys with new capability rows.
  • Domain package (P4-T01): money extensions (bps, percentage, FX with explicit rounding sides, splits), typed error hierarchy mapped to problem types and ledger SQLSTATEs, ledger model with 20 posting templates and an in-memory mirror of 0003_ledger_functions.sql, 17 table-driven state machines with generated Mermaid diagrams and provider status mapping, routing and cut-off evaluator, fee rules and billing planner, limits with tolerance bands, approval tiers; 537 tests, coverage 99.9 % lines, enum-parity test against the db enums and the contract.
  • Observability (P3-T09): @wirebloom/telemetry (OpenTelemetry SDK with health-aware sampling, OTLP traces/metrics/logs, pino correlation, three-layer redaction incl. Luhn/mod-97 scrubbing, low-cardinality metric helpers, Sentry errors-only, --import preload), local stack (collector, Tempo, Loki, Prometheus, Grafana) with six dashboards and 14 alert rules with promtool tests, staged database-outage alert recorded; docs/OBSERVABILITY.md.
  • Web shell with auth (P3-T12): BFF route handler proxy to the API, two-step login with TOTP/passkey/recovery, first-login TOTP enrolment with recovery codes, password reset, email verification, invitation acceptance, choose-account and tenant switcher, step-up modal with request retry, session-expired redirect, impersonation banner, runtime operator branding, notification bell wiring, My details page, next-intl scaffold, TanStack Query data layer; @wirebloom/ui gains IbanInput, PhoneInput, bulk-selection bar, brand theming, hydration-safe Toaster (N19); Playwright login flows (first sign-in, two accounts, expiry, impersonation) green against the real API.
  • Storage and PDF (P3-T13): @wirebloom/storage (S3/MinIO and filesystem object stores, pre-signed PUT with signed type/size/SHA-256 headers, allow-list and magic-byte checks, clamd streaming scanner with fail-closed retries, quarantine, document lifecycle, retention), @wirebloom/pdf (react-pdf statements and confirmations with bundled fonts and byte-identical golden files), API documents module (intent → complete → scan → download; infected → 422 document-not-clean, audited), worker jobs documents.scan/render/retention; migration 0010; compose profile storage (MinIO, ClamAV).
  • Tenancy and authorization (P3-T06): real TenantGuard with membership/API-key/platform (X-Access-Reason) resolution, per-request RLS transaction interceptor, CASL abilities from role_capabilities + operator overrides + custom roles + feature flags, CapabilityGuard, impersonation policy, tenants module (operators, customers, people, teams, invitations, settings, primary-owner transfer, closure), /me completion, staff MFA reset, Postgres stores wired via AppModule.withPostgres, migration 0008 (refresh tokens, custom roles, primary-owner transfers, purge function), 50 tenancy e2e tests, api coverage floor 85%.
  • Notifications (P3-T14): typed template registry (15 React Email templates + digest) with operator branding, preference resolution with locked security categories, transports resend/smtp/capture, Expo push with receipts, Web Push (RFC 8291) with host allow-list, outbox-driven worker jobs (dispatch, deliver email/push/webpush, digest, receipts, retention), API centre/preferences/devices/test-send, Resend webhook handler with suppression list; migration 0009.
  • Infrastructure (P3-T03): Terraform for staging/production in eu-west-2: network with fixed NAT EIPs, KMS, Secrets Manager containers, S3, RDS Multi-AZ, internal ALB + WAF, ECS Fargate (read-only root, circuit breaker, autoscaling, migrate task), CloudFront VPC origins, Route53/ACM, GitHub OIDC roles, ECR in a tooling account, CloudWatch alarms, budgets, AWS Backup with cross-region copies; bootstrap and tooling stacks; tftest, tflint, checkov, Trivy clean; docs/ENVIRONMENT.md.
  • Identity (P3-T05): @wirebloom/auth on better-auth with hashed-session adapter, Argon2id, password policy with breach check, TOTP (RFC 6238), WebAuthn passkeys, recovery codes, PKCE, 15-minute access tokens with rotating refresh and reuse detection, step-up; API identity module (24 /auth routes, /me, impersonation with reason and audit), AuthGuard before RateLimitGuard, lockouts, invitations with approved domains, request-scoped DatabaseService running requests inside RLS transactions as wb_app; 12 e2e scenarios on Postgres.
  • Test harness (P3-T10): @wirebloom/testkit (seeded factories, template-cloned test databases as wb_app, fake clock, notification capture, HTTP client builder, provider contract wrapper), @wirebloom/vitest-config presets with per-package coverage floors and merged coverage gate, platform/e2e Playwright project (desktop/phone, console guard, API booted on a seeded database, 13 critical flows catalogued, pending ones skipped with the enabling task); CI runs the coverage gate and both e2e suites.
  • Provider layer (P3-T15): @wirebloom/providers full BankingProvider interface per INTEGRATIONS §2, typed ProviderError, registry and client factory, HTTP client with host allow-list, retries with jitter and per-connection circuit breaker, sandbox adapter implementing every method, Banking Circle and Integrated Finance config schemas and capability skeletons, contract suite; @wirebloom/crypto envelope encryption (local and AWS KMS backends, rotation, fingerprints); API provider connections, routing rules with RoutingService.resolve, provider events and replay, KYC connections, inbound webhook receiver with Sumsub/Resend verifiers; migration 0007.
  • Contract: ProviderConnection.credentialsFingerprint/webhookSecretSet, capability flags bacs/returns/statementsCamt053/currencies, GET /routes/{routeId}, nullable ProviderEvent.connectionId, KYC connection environment, sandbox webhook slug.
  • Worker and jobs (P3-T08): @wirebloom/jobs (typed definitions, envelopes, registry, run recorder, advisory-lock service, catalogue of 8 built-in jobs), worker runtime on pg-boss with DLQs, heartbeat health check, graceful shutdown, dead-letter CLI; Postgres stores for idempotency, rate limits, audit, feature flags, outbox and maintenance in @wirebloom/db/stores; migrations 0005/0006.
  • Design system (P3-T11): @wirebloom/ui tokens generated to CSS/Tailwind with AA contrast test, 45 components, Storybook (168 stories, axe on each), web shell with Home/Transactions/Recipients/Payments/Settings sample pages, search-param drawers, step-rail wizard, bottom nav, self-hosted fonts, Playwright smoke suite (17 tests).
  • Data layer (P3-T04): @wirebloom/db with Drizzle schema for all 8 schemas (79 tables, RLS on every table, composite tenant FKs), roles wb_migrator/wb_app/wb_platform, migrations with advisory lock, ledger functions (post_entry, holds, reverse_entry, verify_integrity) with immutability triggers, partitioned append-only audit with sealed hash chain, seed, 40 tests incl. RLS isolation and posting invariants; real database health check.
  • CI/CD (P3-T02): root GitHub Actions (CI with Postgres service, migrations idempotence, Redocly/Spectral, gitleaks, audit, Trivy, image builds; staging and production deploys behind AWS_DEPLOY_ENABLED, OIDC only, rollback), distroless Dockerfiles for api/worker/web, ECS deploy helper, git hooks (lint-staged, Conventional Commits), Dependabot, CODEOWNERS, PR template.
  • API skeleton (P3-T07): module per contract tag, cursor pagination, idempotency interceptor, ETag/If-Match, problem-type registry (46 types), rate-limit guard per SECURITY §1.3, capability/step-up/feature-flag metadata, audit emitter, API-key parsing, contract-drift test against docs/api/openapi.yaml; typed client @wirebloom/contracts/client on openapi-fetch with checked-in generated types.
  • platform/ monorepo scaffold (P3-T01): NestJS 11 (Fastify) API with problem details, zod validation, request context and health; pg-boss worker; Next 15.5 web shell with green tokens and shadcn; Expo SDK 54 mobile; packages domain (Money), contracts, providers (interface + sandbox + contract suite), ui, notifications (Resend, React Email); Terraform skeleton; docker-compose; 126 tests green.
Changed (10)
  • Phase 10 close-out (commits ae627b9, b07d896, 2026-09-24): contract 1.0.0-draft.14 (526 paths, 660 operations, 849 schemas; additive Hold.subject.type = payment_review, x-step-up on replaceOperatorSettings and replacePlatformComplianceDefaults; integrator notes in api/INTEGRATORS.md "Changes in draft.14"); the published contract (developer portal, /openapi.json) is the integrator subset only (105 paths, 126 operations, 206 schemas; no /ops, /platform, /compliance, /support, /internal, /mcp paths, no x-capability / x-step-up / x-feature-flag), the full contract is served only with DOCS_ENABLED and a staff session (W3-09, D-52); customer-facing hold listings present compliance-gate and recall-case holds with subject payment_review and a neutral description (P10R-01, D-53); migrations 0048 and 0049 (when 1790850000000). MASTER_PLAN: P5-T07…P5-T10, P9-T07, P9-T08b, P9-T09…P9-T11, P10-T01…P10-T06, P10-T08 and P10-T07 Completed (reviews/PHASE10_REVIEW.md §9), P9-T08 In Progress (N226); Phase 11 / 12 residual wave dispatched.
  • Phase 10 wave 2 integration (commit 74ce609): one customer document inbox /document-requests for partner-case and EDD requests (the interim EDD /compliance/document-requests routes were removed before release); one shared tipping-off guard for staff-written customer text (422 tipping_off); compliance.incoming runs in the worker @money queue group (D-51); purpose codes outside CNH accept any ISO ExternalPurpose1Code plus IPRT (W1-19); kyb.requireAllPersonsVerified changes need a fresh step-up and no longer skip the customer's own KYC (W1-17); if.backfill, if.sync.drift, cases.apply-event, treasury.execute are stately queues (DEPLOYMENT §4.14); INTEGRATORS "Changes in draft.11" and "Changes in draft.12".
  • Provider connections (P5-T07, D-31/D-32): one active connection per operator and environment; lifecycle draft → configured → testing → active → draining → retired replaces connection_status for banking connections (status no longer writable through PATCH); routes become rail selection within the active connection (migration 0038 re-keys routes_selection_unique per connection); supersedes the multi-connection routing of D-18 / P3-T15. Banking Circle adapter follows the recovered OpenAPI (P5-T10): Booked vs Processed semantics, bookings v3, reconciliation report as the nightly feed. Contract 1.0.0-draft.10 → draft.11.
  • Direction: rebuild from scratch as a multi-operator white-label SaaS banking platform (owner decision, D-00, D-13).
  • Git remote switched to https://github.com/implicitlabs/Wirebloom-Banking.git; old remote removed.
  • Legacy system moved to backup/ (git history preserved).
  • ARCHITECTURE aligned with the API contract: webhook path /v1/webhooks/{provider}/{token}, ledger kinds, payment action routes, resource names; provider id naming rule (kebab-case in code and API, snake_case in the database enum).
  • Visual identity set to WireBloom green (owner instruction); Equals Money interaction model retained.
  • Integration fix pass (backend, N59): Device.current computed on GET /devices from the new tenant.notification_devices.session_id (migration 0025, nullable FK to iam.sessions, set null on delete, idempotent; set on every POST /devices); the worker exchange jobs no longer request the old order-confirmation render and the unused ExchangesService.confirmation() path is removed (GET /exchanges/{id}/confirmation renders the PDF on demand; exchange_status / statement_ready notifications unchanged); worker notification tests expect the contract NotificationData (tenantId, template) in in-app and push data; money-gaps.e2e formatted.
  • Bulk payment submission is asynchronous (scalability audit X-12, D-38; commit c3bc350): POST /bulk-payments/{bulkPaymentId}/submit returns 202 BulkPaymentSubmitAccepted (the Job plus the batch in submitting, Location: /v1/jobs/{jobId}) and a resumable worker runner creates the payments; contract 1.0.0-draft.10. Timeouts by default (D-37): database statement / lock / idle-in-transaction, Fastify request, provider transport, BFF and mobile fetch budgets, config-driven; long jobs raise them with SET LOCAL.
Fixed (7)
  • Phase 10 close-out blockers (commit b07d896, 2026-09-24; reviews/PHASE10_REVIEW.md §8: 10 / 10 verified-closed, 0 reopened; gate 118/118): tipping-off in the customer holds view (P10R-01, payment_review subject); remaining Banking Circle registration, token-rotation and deactivate calls moved out of the request transaction with a change lease on providers.webhook_endpoints (P10R-02, migration 0049; N193 resolved); provider cost upsert shares the monthly close lock and post_close follows the line's month (P10R-03); open-banking refresh never mass-retires on a page cap, revoke intents expire (P10R-04); VIBAN details sync isolates failures, stale re-issue sweeper, stuck order intent alert (P10R-05); one-click unsubscribe limited per token (P10R-06); step-up on sensitive operator settings and platform compliance defaults with access reason, fresh step-up refuses without a session (P10R-07 a / b / d); developer registration timing oracle removed (P10R-08); concurrent status-page link answers 409 (W3-13). New residuals N298…N307.
  • Phase 10 wave 3 review findings (commit ae627b9, 2026-09-24; reviews/PHASE10_WAVE3_REVIEW.md; gate 118/118): open-banking calls outside the request transaction with a committed revoke intent (W3-01); key-created customers gated by an invitation domain list and a rate limit (W3-02); step-up guard refuses API keys unless a route policy allows it, owners notified on webhook endpoint changes (W3-03, P4 S-14); operator-only simulated events refused for customer keys (W3-04); registration e-mail enumeration removed (W3-05); simulations and developer registration outside ambient transactions (W3-06, W3-07); only the integrator subset of the contract public (W3-09); stable unsubscribe tokens, RFC 8058 one-click, no incident cascade (W3-10…W3-14); step-up on platform writes, support role trimmed (W3-15); VIBAN reservation, re-issue, order intent and single-flight details push (W3-16…W3-20); late provider cost lines stamped post-close with alerts, cross-currency margin (W3-21…W3-23); TypeScript SDK sample app in CI (W3-27); operator gate rows re-floored by migration 0048 with step-up on the PUT (W3-28). Unfixed Low / Info rows in N297.
  • Phase 10 wave 3 (commit 95c6ded): wave 2 review fix-forward: BC recall accept records the intent before the partner call and reverses in its own transaction, partner-side ACCEPTED closure reverses or alerts (W2-01…W2-03); gate settings and hold rules floored by the platform (W2-04); incoming holds guarded in the DB (W2-05, 0047); IF decisions pushed outside the transaction (W2-06); sync status adoption through the customer and balance state machines (W2-07, W2-08); screening fails closed (W2-09); incoming credits screened before funds are available (W2-10); RFI answer outside the transaction (W2-12); batched recipient screening (W2-21); identity re-check path (W2-27); compliance tag in API.md (W2-30); worker README jobs (W2-53); unconfirmed treasury execution opens a reconciliation exception (W2-48 partial). Direct-debit signedAt date bug; N85, N86, N163.
  • Phase 10 wave 2 (commit 74ce609): N93 Integrated Finance hold captured on outgoing-transfer-processing (reversed when a failure follows); cards webhook routing (pre-existing): card events stored under their provider type (cards/…, card-transactions/…) now reach cards.apply-event (D-51); wave 1 review fixes W1-01 (BC incoming credit double-post between webhook and reconciliation), W1-02 / W1-03 (treasury FX idempotency, claims and ledger holds), W1-04 (IF webhook key shadowing), W1-05 / W1-14 (BC return ordered before the ledger reversal, AlreadyReturned recovery, partner payment id), W1-06 (BC direct-debit townName), W1-07 / W1-08 (individual names from verified personal data, identity locked after applicant creation), W1-09 / W1-39 (BC IP allow-list at ingestion, live config regressions refused), W1-10 (deactivate counts mandates and settlement mirrors), W1-11 partly (per-connection webhook registration lock), W1-13 (BC credential rotation keeps webhook keys), W1-20 (draft.11 change notes), W1-21 / W1-22 (race, refusal and sole-trader payout tests, fixture citations).
  • Phase 10 wave 1 (commit d4cea6c): N88 onboarding provisions only the active connection of the environment (provisioning holds released on activation); N89 Banking Circle returns call POST /api/v1/returns; N90 all 17 PaymentStatus values mapped (ScaPending spelling, Hold → compliance attention); N91 the 13 inferred Banking Circle fixtures rewritten and schema-validated against the recovered OpenAPI; N92 Integrated Finance V1/V2 webhook keys per environment built in; N87 closed by the console subscription lifecycle. Flaky tests: telemetry smoke hook timeouts; worker vitest timeouts and a template-database clone per suite.
  • Re-review follow-ups (PHASE11_12_REREVIEW.md): transient PostgreSQL errors (55P03/57014/40P01) answered as retryable 503 and retried in the worker; error serializer shared via @wirebloom/telemetry/errors in API, worker and MCP; migration 0034 idempotent with a CONCURRENTLY pre-build step; stately pg-boss policy for reconciliation and billing queues; BFF timeout applies to headers and idle body only; mobile token/revoke fetch timeouts; CSP upload origins reject wildcards; @ImpersonationAllowed removed; DSR pre-check mirrors the shared-identity rule.
  • Security and scalability audit fixes (commit c3bc350, 2026-09-24; full gate 106/106; Coord re-review reviews/PHASE11_12_REREVIEW.md: 0 FAIL, 4 PASS, 19 PASS-WITH-NOTES, follow-ups R-01…R-17). Security (reviews/SECURITY_AUDIT_2026-09.md): S-01 recipient identifiers bound at approval and re-checked at submission (recipient_changed; 409 on recipient edits while payments are in flight), S-02 limit-queued payments reopen the customer approval on staff release, S-03 DSR erasure keeps identities shared with other operators, S-04 role passwords set as client-side SCRAM verifiers, S-05 impersonation blocks self.profile writes, S-06 per-customer advisory lock around limit checks, S-10 CSP connect-src includes the S3 upload origin, S-11 BFF body cap (413) and streamed upstream responses, S-12 SQL parameters scrubbed from API error logs. Scalability (reviews/SCALABILITY_AUDIT_2026-09.md): X-01 per-operator incremental ledger integrity with checkpoints, X-02 set-based audit sealing and checkpointed chain verification, X-07 timeouts by default, X-11 (partial) partner calls moved out of the request transaction for quotes, recipient approval and KYB, X-12 asynchronous bulk submit, X-13 statements, X-14 reconciliation and X-15 (partial) billing fanned out per operator, X-16 (partial) and X-17 retention purges drained in bounded batches with supporting indexes, X-40 BFF/RSC upstream timeout, X-41 mobile fetch timeout. Migration 0034_scale_readiness (journal when 1790430000000). Residual items and follow-ups: MASTER_PLAN.md P11/P12 rows, KNOWN_ISSUES N156…N159.
Security (1)
  • Legacy exposures documented (12 Critical); containment tasks P1-T01…P1-T12 defined and blocked on owner access.