openapi: 3.1.0
info:
  title: VEREID Identity API
  version: 1.0.0
  description: |
    Hosted identity verification API (Persona/Veriff competitor).

    **Honest tier labels** (do not overclaim):
    - T0: Anonymous (no checks)
    - T1: Photo (selfie + ID front match via AWS Rekognition + Textract OCR)
    - T2: Liveness (T1 + challenge-response liveness, anti-spoof)
    - T3: Sanctions-clear (T2 + OFAC/EU/UK/UN/AU consolidated free feeds)
    - T4: Business (T3 + GLEIF / SEC EDGAR / UK Companies House / AU ABN)
    - T5: Chip-auth (T2 + ePassport NFC Passive Authentication only — does NOT
      claim issuing-state verification. Wording: "Document is genuine and
      unmodified per ICAO 9303 Passive Authentication".)
    - T6: Gov-record (per-country direct gov check — deferred post-MVP)
  contact:
    name: VEREID
    url: https://id.vereid.com
  license:
    name: Proprietary
servers:
  - url: https://api.vereid.com
    description: Production
  - url: https://api.staging.vereid.com
    description: Staging
security:
  - apiKey: []
tags:
  - name: sessions
    description: Verification sessions
  - name: webhooks
    description: Outbound webhook configuration
  - name: admin
    description: Admin operations
paths:
  /v1/verify/sessions:
    post:
      tags: [sessions]
      summary: Create a verification session
      description: |
        Creates a new verification session and returns a hosted URL the end
        user can be redirected to.
      operationId: createSession
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateSessionRequest"
      responses:
        "201":
          description: Session created
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Session"
        "400":
          $ref: "#/components/responses/BadRequest"
        "401":
          $ref: "#/components/responses/Unauthorized"
  /v1/verify/sessions/{id}:
    parameters:
      - $ref: "#/components/parameters/SessionId"
    get:
      tags: [sessions]
      summary: Get verification session status
      operationId: getSession
      responses:
        "200":
          description: Session
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Session"
        "404":
          $ref: "#/components/responses/NotFound"
  /v1/verify/webhooks:
    post:
      tags: [webhooks]
      summary: Register an outbound webhook
      description: |
        Register a URL to receive `verification.completed`, `verification.failed`,
        and `verification.manual_review` events. Payloads are HMAC-signed with
        header `Vereid-Signature: v1,t={unix_ts},sig={hex_hmac_sha256}`.
      operationId: createWebhook
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebhookRequest"
      responses:
        "201":
          description: Webhook registered
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Webhook"
  /v1/verify/manual-review/{id}:
    parameters:
      - $ref: "#/components/parameters/SessionId"
    post:
      tags: [admin]
      summary: Admin override for a session in manual_review
      operationId: manualReview
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/ManualReviewRequest"
      responses:
        "200":
          description: Override applied
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Session"
components:
  securitySchemes:
    apiKey:
      type: apiKey
      in: header
      name: Authorization
      description: Bearer API key, e.g. `Bearer vid_live_...`
  parameters:
    SessionId:
      name: id
      in: path
      required: true
      schema:
        type: string
        pattern: "^vses_[A-Za-z0-9]{16,}$"
  responses:
    BadRequest:
      description: Invalid request
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    Unauthorized:
      description: Invalid or missing API key
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
    NotFound:
      description: Resource not found
      content:
        application/json:
          schema:
            $ref: "#/components/schemas/Error"
  schemas:
    Tier:
      type: string
      enum: [T0, T1, T2, T3, T4, T5, T6]
      description: Honest verification tier label.
    CreateSessionRequest:
      type: object
      required: [tier, flow_id]
      properties:
        flow_id:
          type: string
          description: ID of the verification flow template to use.
        tier:
          $ref: "#/components/schemas/Tier"
        reference_id:
          type: string
          description: Customer-supplied reference (e.g. their internal user id).
        redirect_url:
          type: string
          format: uri
          description: Where to send the user after success/failure.
        locale:
          type: string
          default: en-US
        metadata:
          type: object
          additionalProperties: true
    Session:
      type: object
      required: [id, hosted_url, status, tier, created_at]
      properties:
        id:
          type: string
        hosted_url:
          type: string
          format: uri
        status:
          type: string
          enum:
            [
              created,
              in_progress,
              processing,
              success,
              failed,
              manual_review,
              expired,
              withdrawn,
            ]
        tier:
          $ref: "#/components/schemas/Tier"
        reference_id:
          type: string
        result:
          $ref: "#/components/schemas/Result"
        created_at:
          type: string
          format: date-time
        completed_at:
          type: string
          format: date-time
          nullable: true
        expires_at:
          type: string
          format: date-time
    Result:
      type: object
      properties:
        decision:
          type: string
          enum: [approved, declined, review]
        achieved_tier:
          $ref: "#/components/schemas/Tier"
        confidence:
          type: number
          minimum: 0
          maximum: 1
        checks:
          type: array
          items:
            $ref: "#/components/schemas/Check"
        evidence_urls:
          type: array
          description: Pre-signed URLs to evidence (PII-VAULT, time-limited).
          items:
            type: string
            format: uri
    Check:
      type: object
      required: [name, status]
      properties:
        name:
          type: string
          example: face_match
        status:
          type: string
          enum: [pass, fail, skipped, error]
        score:
          type: number
        reason:
          type: string
          description: |
            For T5: when applicable, MUST be the literal string
            "Document genuine per ICAO 9303 Passive Authentication".
    CreateWebhookRequest:
      type: object
      required: [url, events]
      properties:
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
            enum:
              - verification.completed
              - verification.failed
              - verification.manual_review
    Webhook:
      type: object
      required: [id, url, events, signing_secret, created_at]
      properties:
        id:
          type: string
        url:
          type: string
          format: uri
        events:
          type: array
          items:
            type: string
        signing_secret:
          type: string
          description: HMAC signing secret. Shown ONCE on create.
        created_at:
          type: string
          format: date-time
    ManualReviewRequest:
      type: object
      required: [decision, reason]
      properties:
        decision:
          type: string
          enum: [approve, reject]
        reason:
          type: string
          minLength: 3
        reviewer_id:
          type: string
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [code, message]
          properties:
            code:
              type: string
            message:
              type: string
            request_id:
              type: string
