{"openapi":"3.0.3","info":{"title":"Codex Customer Onboarding API","version":"1.0.0-proposal","description":"# Codex Customer Onboarding API\n\nA proposed extension to the Codex FX API: programmatic customer onboarding, replacing the current form-based intake. Designed to compose with the existing API — a customer approved here appears in `GET /api/v1/customers` and can immediately transact.\n\n## Design principles\n\n- **Proofs, not documents.** Requirements are expressed as proof categories (`entity_formation`, `tax_registered`, ...). Any accepted local document, public-registry enrichment, or structured field can satisfy a proof. Adding a country adds registry rows, not endpoints.\n- **Registry enrichment first.** Where a public registry exists (Receita Federal for BR, FINTRAC for CA MSBs, Companies House for GB), the proof is satisfied live at create time and never becomes a document request.\n- **AI classification at upload.** Every document is classified and parsed synchronously (type, entity name, dates), with freshness and name-consistency rules enforced at the API boundary — a stale or mismatched document is rejected with the reason, not discovered at review.\n- **Progressive by default.** Gates decide first movement (a capped approval); deferred proofs collect while capped rails run. Test-tier approval requires four gates and zero documents.\n- **RFIs are generated, not written.** An RFI is the set of open proofs at submit time; items auto-close when the proof satisfies.\n\n## Webhooks\n\nReuses the existing `POST /api/v1/webhook-subscriber` mechanism. New event types: `onboarding.customer.created`, `onboarding.document.classified`, `onboarding.customer.approved`, `onboarding.rfi.opened`, `onboarding.rfi.closed`.\n\n## Prototype\n\nThis reference implementation runs live at `https://codex-api.omniboarder.com` (any bearer token accepted). Approved customers land in the same canonical registry that drives the Omniboarder demo suite."},"servers":[{"url":"https://codex-api.omniboarder.com","description":"Live prototype (any bearer token)"},{"url":"https://api.codex-sandbox.xyz","description":"Codex sandbox (proposed)"},{"url":"https://api.codex.xyz","description":"Codex production (proposed)"}],"tags":[{"name":"Onboarding Customers"},{"name":"Persons"},{"name":"Documents"},{"name":"Decisions"},{"name":"RFIs"},{"name":"Onboarding Links"}],"components":{"securitySchemes":{"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"JWT","description":"JWT token for API authentication. Include the token in the Authorization header."}}},"paths":{"/api/v1/onboarding/customers":{"post":{"tags":["Onboarding Customers"],"summary":"Create an onboarding customer","description":"Creates a customer in DRAFT and computes its requirement set (proofs) from country, business type and expected volume. Where a live public registry is available, enrichment runs inline: for BR, the CNPJ is verified against Receita Federal and `entity_formation` + `entity_active_status` are satisfied with zero documents.","security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Customer created","content":{"application/json":{"schema":{"title":"OnboardingCustomer","type":"object","description":"A customer moving through KYB onboarding. On approval it becomes visible as a standard Codex FX Customer (GET /api/v1/customers).","properties":{"customerId":{"type":"string"},"legalEntityName":{"type":"string"},"countryCode":{"type":"string","description":"ISO 3166-1 alpha-2"},"businessType":{"type":"string","enum":["COMPANY","FINANCIAL_INSTITUTION","SOLE_PROPRIETOR"]},"registrationNumber":{"nullable":true,"type":"string"},"website":{"nullable":true,"type":"string"},"expectedMonthlyVolumeUsd":{"nullable":true,"type":"number"},"requestedRails":{"type":"array","items":{"type":"string","enum":["USDC_PAYOUT","USDT_PAYOUT","USD_WIRE","EUR_SWIFT"]}},"status":{"type":"string","enum":["DRAFT","IN_REVIEW","RFI_REQUIRED","APPROVED_CAPPED","APPROVED","DECLINED"]},"riskTier":{"nullable":true,"type":"string","enum":["LOW","MODERATE","HIGH"]},"facilityUsd":{"nullable":true,"type":"number","description":"Prefunding facility granted on approval"},"registryEnrichment":{"nullable":true,"type":"object","additionalProperties":true,"description":"Live public-registry result (e.g. Receita Federal for BR, FINTRAC for CA MSBs) captured at create time — proofs it satisfies never become document requests"},"requirements":{"type":"array","items":{"title":"Requirement","type":"object","description":"A proof-category requirement. Requirements are expressed as proofs (what must be true), never as document names — any accepted document, registry enrichment, or structured field can satisfy one.","properties":{"proof":{"type":"string","description":"Canonical proof-category id, e.g. entity_formation"},"phase":{"type":"string","enum":["GATE","DEFERRED"],"description":"GATE blocks first movement; DEFERRED collects while capped rails run"},"status":{"type":"string","enum":["OPEN","SATISFIED"]},"satisfiedBy":{"nullable":true,"type":"string","description":"What satisfied it: a document id, 'registry_enrichment', or a structured field"}},"required":["proof","phase","status","satisfiedBy"],"additionalProperties":false}},"createdAt":{"type":"string","format":"date-time"}},"required":["customerId","legalEntityName","countryCode","businessType","status","requirements","createdAt"],"additionalProperties":false,"example":{"customerId":"cust_ob_k3x9w2","legalEntityName":"TAPPAPAY LTD","countryCode":"CM","businessType":"COMPANY","registrationNumber":"RC/DLA/2021/B/1204","website":"https://tappapay.example","expectedMonthlyVolumeUsd":12000000,"requestedRails":["USDC_PAYOUT","USDT_PAYOUT"],"status":"DRAFT","riskTier":"MODERATE","facilityUsd":null,"registryEnrichment":null,"requirements":[{"proof":"entity_formation","phase":"GATE","status":"OPEN","satisfiedBy":null}],"createdAt":"2026-08-10T12:00:00.000Z"}}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"legalEntityName":{"type":"string","minLength":1,"maxLength":500},"countryCode":{"type":"string","minLength":2,"maxLength":2},"businessType":{"type":"string","enum":["COMPANY","FINANCIAL_INSTITUTION","SOLE_PROPRIETOR"]},"registrationNumber":{"nullable":true,"type":"string","description":"Company registration / tax number. BR: CNPJ (verified live). CA FIs: FINTRAC MSB number (verified live)."},"website":{"nullable":true,"type":"string"},"expectedMonthlyVolumeUsd":{"nullable":true,"type":"number","description":">= 10,000,000 activates the funds_flow_documented gate"},"requestedRails":{"type":"array","items":{"type":"string","enum":["USDC_PAYOUT","USDT_PAYOUT","USD_WIRE","EUR_SWIFT"]}},"accountPurpose":{"nullable":true,"type":"string"},"idempotencyKey":{"type":"string","minLength":1,"maxLength":255,"pattern":"^[A-Za-z0-9_\\-:.]+$"}},"required":["legalEntityName","countryCode","businessType"],"additionalProperties":false}}}}},"get":{"tags":["Onboarding Customers"],"summary":"List onboarding customers","description":"Cursor pagination matching the Codex FX convention.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"title":"OnboardingCustomer","type":"object","description":"A customer moving through KYB onboarding. On approval it becomes visible as a standard Codex FX Customer (GET /api/v1/customers).","properties":{"customerId":{"type":"string"},"legalEntityName":{"type":"string"},"countryCode":{"type":"string","description":"ISO 3166-1 alpha-2"},"businessType":{"type":"string","enum":["COMPANY","FINANCIAL_INSTITUTION","SOLE_PROPRIETOR"]},"registrationNumber":{"nullable":true,"type":"string"},"website":{"nullable":true,"type":"string"},"expectedMonthlyVolumeUsd":{"nullable":true,"type":"number"},"requestedRails":{"type":"array","items":{"type":"string","enum":["USDC_PAYOUT","USDT_PAYOUT","USD_WIRE","EUR_SWIFT"]}},"status":{"type":"string","enum":["DRAFT","IN_REVIEW","RFI_REQUIRED","APPROVED_CAPPED","APPROVED","DECLINED"]},"riskTier":{"nullable":true,"type":"string","enum":["LOW","MODERATE","HIGH"]},"facilityUsd":{"nullable":true,"type":"number","description":"Prefunding facility granted on approval"},"registryEnrichment":{"nullable":true,"type":"object","additionalProperties":true,"description":"Live public-registry result (e.g. Receita Federal for BR, FINTRAC for CA MSBs) captured at create time — proofs it satisfies never become document requests"},"requirements":{"type":"array","items":{"title":"Requirement","type":"object","description":"A proof-category requirement. Requirements are expressed as proofs (what must be true), never as document names — any accepted document, registry enrichment, or structured field can satisfy one.","properties":{"proof":{"type":"string","description":"Canonical proof-category id, e.g. entity_formation"},"phase":{"type":"string","enum":["GATE","DEFERRED"],"description":"GATE blocks first movement; DEFERRED collects while capped rails run"},"status":{"type":"string","enum":["OPEN","SATISFIED"]},"satisfiedBy":{"nullable":true,"type":"string","description":"What satisfied it: a document id, 'registry_enrichment', or a structured field"}},"required":["proof","phase","status","satisfiedBy"],"additionalProperties":false}},"createdAt":{"type":"string","format":"date-time"}},"required":["customerId","legalEntityName","countryCode","businessType","status","requirements","createdAt"],"additionalProperties":false,"example":{"customerId":"cust_ob_k3x9w2","legalEntityName":"TAPPAPAY LTD","countryCode":"CM","businessType":"COMPANY","registrationNumber":"RC/DLA/2021/B/1204","website":"https://tappapay.example","expectedMonthlyVolumeUsd":12000000,"requestedRails":["USDC_PAYOUT","USDT_PAYOUT"],"status":"DRAFT","riskTier":"MODERATE","facilityUsd":null,"registryEnrichment":null,"requirements":[{"proof":"entity_formation","phase":"GATE","status":"OPEN","satisfiedBy":null}],"createdAt":"2026-08-10T12:00:00.000Z"}}},"hasMore":{"type":"boolean"}},"required":["data","hasMore"],"additionalProperties":false}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"parameters":[{"name":"startingAfter","in":"query","required":false,"schema":{"type":"string"}},{"name":"endingBefore","in":"query","required":false,"schema":{"type":"string"}},{"name":"limit","in":"query","required":false,"schema":{"type":"integer"}}]}},"/api/v1/onboarding/customers/{customerId}":{"get":{"tags":["Onboarding Customers"],"summary":"Get an onboarding customer","description":"Returns the customer with its live requirement set.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"title":"OnboardingCustomer","type":"object","description":"A customer moving through KYB onboarding. On approval it becomes visible as a standard Codex FX Customer (GET /api/v1/customers).","properties":{"customerId":{"type":"string"},"legalEntityName":{"type":"string"},"countryCode":{"type":"string","description":"ISO 3166-1 alpha-2"},"businessType":{"type":"string","enum":["COMPANY","FINANCIAL_INSTITUTION","SOLE_PROPRIETOR"]},"registrationNumber":{"nullable":true,"type":"string"},"website":{"nullable":true,"type":"string"},"expectedMonthlyVolumeUsd":{"nullable":true,"type":"number"},"requestedRails":{"type":"array","items":{"type":"string","enum":["USDC_PAYOUT","USDT_PAYOUT","USD_WIRE","EUR_SWIFT"]}},"status":{"type":"string","enum":["DRAFT","IN_REVIEW","RFI_REQUIRED","APPROVED_CAPPED","APPROVED","DECLINED"]},"riskTier":{"nullable":true,"type":"string","enum":["LOW","MODERATE","HIGH"]},"facilityUsd":{"nullable":true,"type":"number","description":"Prefunding facility granted on approval"},"registryEnrichment":{"nullable":true,"type":"object","additionalProperties":true,"description":"Live public-registry result (e.g. Receita Federal for BR, FINTRAC for CA MSBs) captured at create time — proofs it satisfies never become document requests"},"requirements":{"type":"array","items":{"title":"Requirement","type":"object","description":"A proof-category requirement. Requirements are expressed as proofs (what must be true), never as document names — any accepted document, registry enrichment, or structured field can satisfy one.","properties":{"proof":{"type":"string","description":"Canonical proof-category id, e.g. entity_formation"},"phase":{"type":"string","enum":["GATE","DEFERRED"],"description":"GATE blocks first movement; DEFERRED collects while capped rails run"},"status":{"type":"string","enum":["OPEN","SATISFIED"]},"satisfiedBy":{"nullable":true,"type":"string","description":"What satisfied it: a document id, 'registry_enrichment', or a structured field"}},"required":["proof","phase","status","satisfiedBy"],"additionalProperties":false}},"createdAt":{"type":"string","format":"date-time"}},"required":["customerId","legalEntityName","countryCode","businessType","status","requirements","createdAt"],"additionalProperties":false,"example":{"customerId":"cust_ob_k3x9w2","legalEntityName":"TAPPAPAY LTD","countryCode":"CM","businessType":"COMPANY","registrationNumber":"RC/DLA/2021/B/1204","website":"https://tappapay.example","expectedMonthlyVolumeUsd":12000000,"requestedRails":["USDC_PAYOUT","USDT_PAYOUT"],"status":"DRAFT","riskTier":"MODERATE","facilityUsd":null,"registryEnrichment":null,"requirements":[{"proof":"entity_formation","phase":"GATE","status":"OPEN","satisfiedBy":null}],"createdAt":"2026-08-10T12:00:00.000Z"}}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}}]},"patch":{"tags":["Onboarding Customers"],"summary":"Update an onboarding customer","description":"Updates mutable fields; requirements are recomputed.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"title":"OnboardingCustomer","type":"object","description":"A customer moving through KYB onboarding. On approval it becomes visible as a standard Codex FX Customer (GET /api/v1/customers).","properties":{"customerId":{"type":"string"},"legalEntityName":{"type":"string"},"countryCode":{"type":"string","description":"ISO 3166-1 alpha-2"},"businessType":{"type":"string","enum":["COMPANY","FINANCIAL_INSTITUTION","SOLE_PROPRIETOR"]},"registrationNumber":{"nullable":true,"type":"string"},"website":{"nullable":true,"type":"string"},"expectedMonthlyVolumeUsd":{"nullable":true,"type":"number"},"requestedRails":{"type":"array","items":{"type":"string","enum":["USDC_PAYOUT","USDT_PAYOUT","USD_WIRE","EUR_SWIFT"]}},"status":{"type":"string","enum":["DRAFT","IN_REVIEW","RFI_REQUIRED","APPROVED_CAPPED","APPROVED","DECLINED"]},"riskTier":{"nullable":true,"type":"string","enum":["LOW","MODERATE","HIGH"]},"facilityUsd":{"nullable":true,"type":"number","description":"Prefunding facility granted on approval"},"registryEnrichment":{"nullable":true,"type":"object","additionalProperties":true,"description":"Live public-registry result (e.g. Receita Federal for BR, FINTRAC for CA MSBs) captured at create time — proofs it satisfies never become document requests"},"requirements":{"type":"array","items":{"title":"Requirement","type":"object","description":"A proof-category requirement. Requirements are expressed as proofs (what must be true), never as document names — any accepted document, registry enrichment, or structured field can satisfy one.","properties":{"proof":{"type":"string","description":"Canonical proof-category id, e.g. entity_formation"},"phase":{"type":"string","enum":["GATE","DEFERRED"],"description":"GATE blocks first movement; DEFERRED collects while capped rails run"},"status":{"type":"string","enum":["OPEN","SATISFIED"]},"satisfiedBy":{"nullable":true,"type":"string","description":"What satisfied it: a document id, 'registry_enrichment', or a structured field"}},"required":["proof","phase","status","satisfiedBy"],"additionalProperties":false}},"createdAt":{"type":"string","format":"date-time"}},"required":["customerId","legalEntityName","countryCode","businessType","status","requirements","createdAt"],"additionalProperties":false,"example":{"customerId":"cust_ob_k3x9w2","legalEntityName":"TAPPAPAY LTD","countryCode":"CM","businessType":"COMPANY","registrationNumber":"RC/DLA/2021/B/1204","website":"https://tappapay.example","expectedMonthlyVolumeUsd":12000000,"requestedRails":["USDC_PAYOUT","USDT_PAYOUT"],"status":"DRAFT","riskTier":"MODERATE","facilityUsd":null,"registryEnrichment":null,"requirements":[{"proof":"entity_formation","phase":"GATE","status":"OPEN","satisfiedBy":null}],"createdAt":"2026-08-10T12:00:00.000Z"}}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"website":{"type":"string"},"expectedMonthlyVolumeUsd":{"type":"number"},"accountPurpose":{"type":"string"},"operatingAddress":{"type":"string"}},"additionalProperties":false}}}},"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/v1/onboarding/customers/{customerId}/requirements":{"get":{"tags":["Onboarding Customers"],"summary":"Get requirements","description":"The smart union of Codex's compliance floor and the customer's rails/volume/entity-type, expressed as proofs. GATE items block first movement; DEFERRED items collect while capped rails run.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"data":{"type":"array","items":{"title":"Requirement","type":"object","description":"A proof-category requirement. Requirements are expressed as proofs (what must be true), never as document names — any accepted document, registry enrichment, or structured field can satisfy one.","properties":{"proof":{"type":"string","description":"Canonical proof-category id, e.g. entity_formation"},"phase":{"type":"string","enum":["GATE","DEFERRED"],"description":"GATE blocks first movement; DEFERRED collects while capped rails run"},"status":{"type":"string","enum":["OPEN","SATISFIED"]},"satisfiedBy":{"nullable":true,"type":"string","description":"What satisfied it: a document id, 'registry_enrichment', or a structured field"}},"required":["proof","phase","status","satisfiedBy"],"additionalProperties":false}},"hasMore":{"type":"boolean"}},"required":["data","hasMore"],"additionalProperties":false}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/v1/onboarding/customers/{customerId}/persons":{"post":{"tags":["Persons"],"summary":"Add a person (UBO / director / signer)","description":"Sanctions/PEP screening runs synchronously on entry; a hit returns `screeningStatus: HIT` with EDD questions rather than silently queueing.","security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Person added, screening result inline","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fullName":{"type":"string","minLength":1},"role":{"type":"string","enum":["UBO","DIRECTOR","SIGNER"]},"ownershipPercent":{"nullable":true,"type":"number"}},"required":["fullName","role"],"additionalProperties":false}}}},"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}}]},"get":{"tags":["Persons"],"summary":"List persons","description":"","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/v1/onboarding/customers/{customerId}/documents":{"post":{"tags":["Documents"],"summary":"Upload a document (AI-classified synchronously)","description":"Accepts JPG/PNG/PDF as base64. The document is classified against the jurisdiction's accepted types, fields are extracted, per-document freshness rules are enforced, and the proofs it satisfies are applied — all in the response. A document that is stale, unreadable, or not an accepted type returns `accepted: false` with the reason.","security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Classified","content":{"application/json":{"schema":{"title":"DocumentClassification","type":"object","description":"The result of AI classification and parsing, run synchronously at upload. Documents are classified against the jurisdiction's accepted types; the proofs a document satisfies are computed, and freshness rules are enforced per the document registry.","properties":{"documentId":{"type":"string"},"detectedType":{"type":"string"},"confidence":{"type":"number"},"reasoning":{"nullable":true,"type":"string","description":"First-page title / issuing-authority evidence for the classification"},"entityName":{"nullable":true,"type":"string"},"issueDate":{"nullable":true,"type":"string"},"freshness":{"type":"object","properties":{"ruleDays":{"nullable":true,"type":"number"},"status":{"type":"string","enum":["FRESH","STALE","NOT_APPLICABLE"]}},"required":["ruleDays","status"],"additionalProperties":false},"proofsSatisfied":{"type":"array","items":{"type":"string"}},"accepted":{"type":"boolean"}},"required":["documentId","detectedType","confidence","proofsSatisfied","accepted"],"additionalProperties":false}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fileName":{"type":"string"},"contentBase64":{"type":"string","description":"Raw base64 or data: URL. JPG, PNG, or PDF (PDF read natively)."},"declaredType":{"nullable":true,"type":"string","description":"What the customer believes this is — treated as an authoritative signal, per the classification policy"}},"required":["fileName","contentBase64"],"additionalProperties":false}}}},"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}}]},"get":{"tags":["Documents"],"summary":"List documents","description":"","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/v1/onboarding/customers/{customerId}/persons/{personId}/identity-document":{"post":{"tags":["Documents"],"summary":"Upload a person's identity document","description":"Passport / national ID / licence. Extracts holder details, enforces the identity standards (all four corners visible, not expiring within 6 months) and name-matches against the screened person.","security":[{"bearerAuth":[]}],"responses":{"201":{"description":"ID verified or flagged","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"fileName":{"type":"string"},"contentBase64":{"type":"string"}},"required":["fileName","contentBase64"],"additionalProperties":false}}}},"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}},{"name":"personId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/v1/onboarding/customers/{customerId}/submit":{"post":{"tags":["Decisions"],"summary":"Submit for decision","description":"Requires attestation. If every GATE proof is satisfied the customer is approved with a capped facility (APPROVED_CAPPED) sized to expected volume and appears immediately in `GET /api/v1/customers`; open gates instead open an RFI (RFI_REQUIRED) whose items auto-close as proofs satisfy.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","properties":{"customerId":{"type":"string"},"status":{"type":"string","enum":["APPROVED_CAPPED","RFI_REQUIRED","DECLINED"]},"riskTier":{"type":"string"},"facilityUsd":{"nullable":true,"type":"number"},"openGates":{"type":"array","items":{"type":"string"}},"rfiId":{"nullable":true,"type":"string"}},"required":["customerId","status","openGates"],"additionalProperties":false}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"attestedBy":{"type":"string","minLength":3,"description":"Full name of the authorized signer"}},"required":["attestedBy"],"additionalProperties":false}}}},"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/v1/onboarding/customers/{customerId}/rfis":{"get":{"tags":["RFIs"],"summary":"List RFIs","description":"RFIs are generated from open proofs at submit; items reference the proof they exist to satisfy.","security":[{"bearerAuth":[]}],"responses":{"200":{"description":"Success","content":{"application/json":{"schema":{"type":"object","additionalProperties":true}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"parameters":[{"name":"customerId","in":"path","required":true,"schema":{"type":"string"}}]}},"/api/v1/onboarding/links":{"post":{"tags":["Onboarding Links"],"summary":"Create a hosted onboarding link","description":"Returns a magic link to the hosted onboarding flow (no portal account needed) for customers who won't integrate the API.","security":[{"bearerAuth":[]}],"responses":{"201":{"description":"Link created","content":{"application/json":{"schema":{"type":"object","properties":{"linkId":{"type":"string"},"url":{"type":"string"},"expiresAt":{"type":"string","format":"date-time"}},"required":["linkId","url","expiresAt"],"additionalProperties":false}}}},"400":{"description":"The request was invalid","content":{"application/json":{"schema":{"title":"BadRequestError","description":"The request was invalid","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["BadRequestError"]}},"required":["message","code"],"additionalProperties":false}}}},"401":{"description":"Missing or invalid bearer token","content":{"application/json":{"schema":{"title":"UnauthorizedError","description":"Missing or invalid bearer token","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["UnauthorizedError"]}},"required":["message","code"],"additionalProperties":false}}}},"404":{"description":"The requested resource was not found","content":{"application/json":{"schema":{"title":"NotFoundError","description":"The requested resource was not found","type":"object","properties":{"message":{"type":"string"},"code":{"type":"string","enum":["NotFoundError"]}},"required":["message","code"],"additionalProperties":false}}}}},"requestBody":{"required":true,"content":{"application/json":{"schema":{"type":"object","properties":{"legalEntityName":{"nullable":true,"type":"string"},"countryCode":{"nullable":true,"type":"string"}},"additionalProperties":false}}}}}}}}