openapi: 3.1.0
info:
  title: Threasury Integration and Ecosystem Platform
  version: 5.0.0
  description: >
    Canonical integration boundary. The middleware connects systems; FEI decides;
    the Ledger records; the Platform presents; regulated providers execute.
servers:
  - url: https://api.threasury.org
components:
  securitySchemes:
    ServiceToken:
      type: http
      scheme: bearer
      bearerFormat: JWT
    ApiKey:
      type: apiKey
      in: header
      name: X-API-Key
    MutualTLS:
      type: mutualTLS
    ControlTowerSession:
      type: apiKey
      in: cookie
      name: threasury_gateway_control_tower
  parameters:
    CorrelationId:
      name: X-Correlation-Id
      in: header
      required: true
      schema: {type: string, minLength: 8}
    IdempotencyKey:
      name: Idempotency-Key
      in: header
      required: true
      schema: {type: string, minLength: 8}
    CsrfToken:
      name: X-CSRF-Token
      in: header
      required: true
      schema: {type: string, minLength: 32}
  schemas:
    ZeroTrustContext:
      type: object
      additionalProperties: false
      required: [actor_id, organisation_id, legal_entity_id, tenant_id, capability, purpose, correlation_id, timestamp]
      properties:
        actor_id: {type: string}
        organisation_id: {type: string}
        legal_entity_id: {type: string}
        tenant_id: {type: string}
        capability: {type: string}
        purpose: {type: string}
        consent_reference: {type: [string, "null"]}
        resource_scope: {type: [string, "null"]}
        correlation_id: {type: string}
        timestamp: {type: string, format: date-time}
    CanonicalCommand:
      type: object
      additionalProperties: false
      required: [command_type, idempotency_key, context, payload]
      properties:
        command_id: {type: string}
        command_type:
          enum: [ObtainFinancialResourceData, ValidateConnectedAccount, PreparePaymentInstruction, RetrieveProviderCapability, NotifyInstitution, ReceiveProgrammeInformation, RequestExternalScreening, SearchFinancialInstitutions, CreateBankConnectionSession, CompleteBankConnection, RepairBankConnection, RefreshExternalBalances, SynchroniseExternalTransactions, DisconnectExternalAccount]
        idempotency_key: {type: string}
        causation_id: {type: [string, "null"]}
        provider: {type: [string, "null"]}
        context: {$ref: '#/components/schemas/ZeroTrustContext'}
        payload: {type: object}
    ControlTowerSessionRequest:
      type: object
      additionalProperties: false
      required: [api_key]
      properties:
        api_key: {type: string, minLength: 32, writeOnly: true}
    ConnectedApplication:
      type: object
      additionalProperties: false
      required: [name, app_type, connection_zone, environment, auth_mode, owner, capabilities]
      properties:
        id: {type: string}
        name: {type: string}
        app_type: {enum: [FEI, LEDGER, PLATFORM_BFF, PARTNER_APPLICATION, REGULATED_PROVIDER, INSTITUTIONAL_SERVICE, SCREENING_SERVICE]}
        connection_zone: {enum: [INTERNAL, EXTERNAL, PRESENTATION]}
        environment: {enum: [SANDBOX, PRODUCTION]}
        base_url: {type: [string, "null"], format: uri}
        auth_mode: {enum: [OAUTH_MTLS, MTLS, SIGNED_WEBHOOK, AWS_IAM, PRIVATE_SERVICE, BATCH]}
        owner: {type: string}
        capabilities: {type: array, items: {type: string}, minItems: 1, uniqueItems: true}
        reason: {type: string}
    ApiKeyIssueRequest:
      type: object
      additionalProperties: false
      required: [name, owner_type, owner_id, environment, scopes]
      properties:
        name: {type: string}
        owner_type: {enum: [INTERNAL_SERVICE, PARTNER, OPERATOR]}
        owner_id: {type: string}
        partner_id: {type: [string, "null"]}
        environment: {enum: [SANDBOX, PRODUCTION]}
        scopes: {type: array, items: {type: string}, minItems: 1, uniqueItems: true}
        allowed_cidrs: {type: array, items: {type: string}}
        expires_at: {type: [string, "null"], format: date-time}
paths:
  /oauth2/token:
    post:
      summary: Issue a short-lived, scope-bound client-credentials token
      security: [{MutualTLS: []}]
      responses:
        "200": {description: Access token issued}
        "401": {description: Client, scope, expiry or mTLS binding rejected}
  /v1/health:
    get:
      summary: Gateway health
      responses:
        "200": {description: Healthy}
  /financial-connectivity/v1/institutions:
    get:
      summary: Search institutions through a certified Open Banking adapter
      security: [{ApiKey: []}]
      parameters:
        - {$ref: '#/components/parameters/CorrelationId'}
        - {name: X-Tenant-ID, in: header, required: true, schema: {type: string}}
        - {name: X-Customer-ID, in: header, required: true, schema: {type: string}}
        - {name: X-Purpose, in: header, required: true, schema: {type: string}}
        - {name: query, in: query, required: true, schema: {type: string}}
      responses:
        "200": {description: Provider-neutral institution catalogue}
        "503": {description: No certified or configured adapter is available}
  /financial-connectivity/v1/status:
    get:
      summary: Read the provider-neutral Financial Connectivity activation posture
      security: [{ApiKey: []}]
      parameters:
        - {$ref: '#/components/parameters/CorrelationId'}
        - {name: X-Tenant-ID, in: header, required: true, schema: {type: string}}
        - {name: X-Customer-ID, in: header, required: true, schema: {type: string}}
        - {name: X-Purpose, in: header, required: true, schema: {type: string}}
      responses:
        "200": {description: Feature flag, certified-adapter availability, Threasury consent authority, provider-permission controls and non-payment posture}
  /financial-connectivity/v1/consents:
    post:
      summary: Create a purpose-bound Open Banking consent draft
      security: [{ApiKey: []}]
      parameters:
        - {$ref: '#/components/parameters/CorrelationId'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "201": {description: Governed consent draft created}
  /financial-connectivity/v1/customers/{customer_id}/consents:
    get:
      summary: List a customer's governed Open Banking permissions
      security: [{ApiKey: []}]
      parameters: [{name: customer_id, in: path, required: true, schema: {type: string}}]
      responses:
        "200": {description: Consent lifecycle projection}
  /financial-connectivity/v1/consents/{id}/{action}:
    post:
      summary: Present, grant, evaluate, renew or revoke a consent through a controlled lifecycle transition
      security: [{ApiKey: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {name: action, in: path, required: true, schema: {enum: [present, grant, evaluate, renew, revoke]}}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "200": {description: Consent transition recorded with evidence}
        "403": {description: Purpose, scope, customer or lifecycle policy denied}
  /financial-connectivity/v1/connections/sessions:
    post:
      summary: Create a short-lived provider connection session after consent evaluation
      security: [{ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
      responses:
        "201": {description: Connection session returned to the Platform BFF}
  /financial-connectivity/v1/connections/complete:
    post:
      summary: Exchange a one-time public token inside the adapter and create canonical accounts
      security: [{ApiKey: []}]
      parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
      responses:
        "201": {description: Canonical connection and discovered accounts created}
  /financial-connectivity/v1/connections/{id}/repair-complete:
    post:
      summary: Re-observe provider permission after Link update mode and confirm repair
      security: [{ApiKey: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "200": {description: Provider permission observed and healthy connection restored}
        "503": {description: Provider reauthorisation could not be confirmed}
  /financial-connectivity/v1/customers/{customer_id}/connections:
    get:
      summary: List canonical customer connections and health
      security: [{ApiKey: []}]
      parameters: [{name: customer_id, in: path, required: true, schema: {type: string}}]
      responses: {"200": {description: Provider-neutral connections}}
  /financial-connectivity/v1/customers/{customer_id}/accounts:
    get:
      summary: List canonical external accounts and latest balance snapshots
      security: [{ApiKey: []}]
      parameters: [{name: customer_id, in: path, required: true, schema: {type: string}}]
      responses: {"200": {description: Provider-neutral external financial resources}}
  /financial-connectivity/v1/connections/{id}/accounts:
    patch:
      summary: Apply explicit customer account selection and analysis permission
      security: [{ApiKey: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses: {"200": {description: Account selection recorded}}
  /financial-connectivity/v1/connections/{id}/balances/refresh:
    post:
      summary: Refresh canonical balance snapshots within consent and API budgets
      security: [{ApiKey: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses: {"202": {description: Balance refresh completed and canonical events queued}}
  /financial-connectivity/v1/connections/{id}/transactions/sync:
    post:
      summary: Run cursor-based added, modified and removed transaction synchronisation
      security: [{ApiKey: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses: {"202": {description: Mutable external transaction signals and cursor checkpoint recorded}}
  /financial-connectivity/v1/connections/{id}/health:
    get:
      summary: Retrieve canonical connection health and customer-action posture
      security: [{ApiKey: []}]
      parameters: [{name: id, in: path, required: true, schema: {type: string}}]
      responses: {"200": {description: Connection health}}
  /financial-connectivity/v1/connections/{id}/repair-sessions:
    post:
      summary: Create a provider update-mode session without exposing the access token
      security: [{ApiKey: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses: {"201": {description: Repair session issued}}
  /financial-connectivity/v1/connections/{id}:
    delete:
      summary: Disconnect the provider item and revoke its vaulted credential
      security: [{ApiKey: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses: {"200": {description: Connection disconnected}}
  /internal/v1/catalogue:
    get:
      summary: Canonical capability and certified-adapter catalogue
      security: [{ServiceToken: []}]
      parameters: [{$ref: '#/components/parameters/CorrelationId'}]
      responses:
        "200": {description: Catalogue}
  /internal/v1/commands:
    post:
      summary: Submit a canonical command from FEI or an approved internal service
      security: [{ServiceToken: []}]
      parameters:
        - {$ref: '#/components/parameters/CorrelationId'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/CanonicalCommand'}
      responses:
        "202": {description: Accepted for adapter execution}
        "422": {description: Invalid or authority-crossing command}
  /external/v1/webhooks/providers/{provider}:
    post:
      summary: Receive a signed provider fact
      security: [{MutualTLS: [], ApiKey: []}]
      parameters:
        - name: provider
          in: path
          required: true
          schema: {type: string}
        - {$ref: '#/components/parameters/CorrelationId'}
      responses:
        "202": {description: Normalised event accepted}
        "422": {description: Invalid event}
  /external/v1/batches:
    post:
      summary: Submit an authorised, content-hash-idempotent batch
      security: [{ServiceToken: [], MutualTLS: []}]
      responses:
        "202": {description: Batch accepted with immutable manifest}
        "403": {description: Consent, contract, purpose or scope policy denied}
  /external/v1/batches/{id}:
    get:
      summary: Retrieve batch validation and delivery status
      security: [{ServiceToken: [], MutualTLS: []}]
      parameters:
        - name: id
          in: path
          required: true
          schema: {type: string}
      responses:
        "200": {description: Batch status}
  /admin/v1/registries:
    get:
      summary: Retrieve governed partner, provider and capability registries
      security: [{ServiceToken: []}]
      responses:
        "200": {description: Governed registries}
  /admin/v1/posture:
    get:
      summary: Retrieve persistence, event, credential, policy and audit posture
      security: [{ServiceToken: []}]
      responses:
        "200": {description: Operational posture}
  /admin/v1/events/deliver:
    post:
      summary: Run a bounded durable-delivery cycle
      security: [{ServiceToken: []}]
      responses:
        "200": {description: Delivery results}
  /admin/v1/events/{event_id}/replay-requests:
    post:
      summary: Request maker-checker replay of a retained event
      security: [{ServiceToken: []}]
      parameters:
        - name: event_id
          in: path
          required: true
          schema: {type: string}
      responses:
        "202": {description: Replay awaiting independent approval}
  /developer/v1/applications:
    post:
      summary: Submit a sandbox partner application
      responses:
        "202": {description: Application accepted for due diligence}
        "422": {description: Application validation failed}
  /admin/v1/certificates:
    post:
      summary: Activate a governed certificate and synchronise the trusted edge registry
      security: [{ServiceToken: []}]
      responses:
        "201": {description: Certificate activated}
  /admin/v1/certificates/{id}/revoke:
    post:
      summary: Revoke a certificate at the edge and backend
      security: [{ServiceToken: []}]
      parameters:
        - name: id
          in: path
          required: true
          schema: {type: string}
      responses:
        "200": {description: Certificate revoked}
  /admin/v1/audit/checkpoints:
    post:
      summary: Sign and archive a Merkle audit checkpoint under Object Lock
      security: [{ServiceToken: []}]
      responses:
        "201": {description: Audit checkpoint archived}
  /control-tower/v1/session:
    post:
      summary: Exchange a governed operator key for a short-lived Control Tower session
      security: [{MutualTLS: []}]
      parameters: [{$ref: '#/components/parameters/IdempotencyKey'}]
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/ControlTowerSessionRequest'}
      responses:
        "201": {description: HttpOnly session established and CSRF token returned}
        "401": {description: Credential invalid or missing control-plane permission}
  /control-tower/v1/overview:
    get:
      summary: Retrieve consolidated Gateway operations posture
      security: [{MutualTLS: [], ControlTowerSession: []}]
      responses:
        "200": {description: Connection, event, assurance, certificate and credential posture}
  /control-tower/v1/connected-apps:
    get:
      summary: List governed connected applications
      security: [{MutualTLS: [], ControlTowerSession: []}]
      responses:
        "200": {description: Connected application registry}
    post:
      summary: Submit connected-application configuration for independent approval
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/ConnectedApplication'}
      responses:
        "201": {description: Configuration recorded in fail-closed CONFIGURED state with pending approval}
  /control-tower/v1/connected-apps/{id}/health-check:
    post:
      summary: Run an allow-listed HTTPS health check for an active connection
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "200": {description: Governed health status and latency}
  /control-tower/v1/approvals:
    get:
      summary: List risk-tiered, expiring four-eyes approval requests
      security: [{MutualTLS: [], ControlTowerSession: []}]
      responses:
        "200": {description: Approval queue}
  /control-tower/v1/approvals/{id}/decision:
    post:
      summary: Record an independent approval decision
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              additionalProperties: false
              required: [approved, reason]
              properties:
                approved: {type: boolean}
                reason: {type: string, minLength: 1}
      responses:
        "200": {description: Decision recorded; requester self-approval is rejected}
  /control-tower/v1/compliance-alerts:
    get:
      summary: List evidence-linked compliance alerts
      security: [{MutualTLS: [], ControlTowerSession: []}]
      responses:
        "200": {description: Compliance alert workflow}
    post:
      summary: Raise a compliance alert
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "201": {description: Alert raised and activity evidence appended}
  /control-tower/v1/compliance-alerts/{id}/status:
    post:
      summary: Acknowledge, investigate or resolve a compliance alert
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "200": {description: Alert status updated}
  /control-tower/v1/screenings:
    get:
      summary: List external screening requests and provider outcomes
      security: [{MutualTLS: [], ControlTowerSession: []}]
      responses:
        "200": {description: Screening evidence registry}
    post:
      summary: Route a tokenised screening reference to an active screening-service connection
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "202": {description: Screening queued; regulated provider retains execution authority}
  /control-tower/v1/alert-routes:
    get:
      summary: List governed alert routes
      security: [{MutualTLS: [], ControlTowerSession: []}]
      responses:
        "200": {description: Alert route registry}
    post:
      summary: Submit an alert route for independent approval
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "201": {description: Route held PAUSED until approved}
  /control-tower/v1/partners:
    get:
      summary: Read governed partner, capability and certificate posture
      security: [{MutualTLS: [], ControlTowerSession: []}]
      responses:
        "200": {description: Partner operations registry projection}
  /control-tower/v1/partner-operations:
    get:
      summary: List partner lifecycle operations
      security: [{MutualTLS: [], ControlTowerSession: []}]
      responses:
        "200": {description: Partner operation queue}
    post:
      summary: Request an approval-backed partner operation
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "202": {description: Partner operation and approval request created}
  /control-tower/v1/activity:
    get:
      summary: Read the secret-redacted, SHA-256-linked activity feed
      security: [{MutualTLS: [], ControlTowerSession: []}]
      responses:
        "200": {description: Tamper-evident activity projection}
  /control-tower/v1/api-keys:
    get:
      summary: List governed API-key metadata without secret values or hashes
      security: [{MutualTLS: [], ControlTowerSession: []}]
      responses:
        "200": {description: Credential metadata}
    post:
      summary: Issue a scoped, one-time-reveal API key
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      requestBody:
        required: true
        content:
          application/json:
            schema: {$ref: '#/components/schemas/ApiKeyIssueRequest'}
      responses:
        "201": {description: Credential issued; secret returned once}
  /control-tower/v1/api-keys/{id}/rotate:
    post:
      summary: Rotate an API key with a controlled ten-minute overlap
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "200": {description: New secret returned once; previous secret remains temporarily valid}
  /control-tower/v1/api-keys/{id}/revoke:
    post:
      summary: Revoke an API key immediately
      security: [{MutualTLS: [], ControlTowerSession: []}]
      parameters:
        - {name: id, in: path, required: true, schema: {type: string}}
        - {$ref: '#/components/parameters/CsrfToken'}
        - {$ref: '#/components/parameters/IdempotencyKey'}
      responses:
        "200": {description: Credential revoked}
