openapi: 3.1.0
info:
  title: Stile API
  version: 1.0.0
  description: |
    The Stile API provides age and identity verification, compliance checks,
    and webhook management for regulated industries.

    All timestamps are Unix seconds. All IDs are prefixed strings
    (e.g. `vks_`, `we_`, `evt_`). Responses use snake_case field names.

servers:
  - url: https://api.stile.id
    description: Production
  - url: http://localhost:3000
    description: Local development

security:
  - BearerAuth: []

tags:
  - name: Verification Sessions
    description: Create, retrieve, cancel, and list verification sessions.
  - name: Compliance
    description: Check regulatory requirements for use cases and jurisdictions.
  - name: Verified Person
    description: Server-side lookup of previously verified persons.
  - name: Webhook Endpoints
    description: Manage webhook endpoints for receiving verification events.
  - name: Events
    description: List and retrieve events generated by verification activity.
  - name: Risk
    description: Score the fraud risk of an IP, email, phone, or device.

components:
  securitySchemes:
    BearerAuth:
      type: http
      scheme: bearer
      description: |
        Use a secret API key as the bearer token. Secret keys are prefixed
        with `stile_sk_`; publishable keys with `stile_pk_`.

  schemas:
    # ── Enums ────────────────────────────────────────────────────────
    UseCase:
      type: string
      enum:
        - alcohol_delivery
        - alcohol_in_person
        - gambling_online
        - gambling_in_person
        - cannabis_dispensary
        - cannabis_delivery
        - tobacco_nicotine
        - adult_content
        - social_media
        - firearms
        - general

    VerificationType:
      type: string
      enum: [identity, age, student]

    VerificationMethod:
      type: string
      enum:
        - mdl
        - mid
        - eudi_pid
        - facial_age
        - parental_consent
        - carrier_lookup
        - open_banking
        - student
        - selfie_liveness
        - selfie_match
        - self_attestation
        - document_capture

    CredentialStrength:
      type: string
      enum:
        - self_attestation
        - facial_age
        - carrier_lookup
        - open_banking
        - document_capture
        - mdl
        - mid
        - eudi_pid

    SessionStatus:
      type: string
      enum:
        - created
        - pending
        - processing
        - requires_input
        - verified
        - failed
        - cancelled
        - expired

    # ── Error ────────────────────────────────────────────────────────
    Error:
      type: object
      required: [error]
      properties:
        error:
          type: object
          required: [type, code, message]
          properties:
            type:
              type: string
              enum:
                - invalid_request_error
                - authentication_error
                - rate_limit_error
                - api_error
            code:
              type: string
              description: Machine-readable error code.
            message:
              type: string
              description: Human-readable error message.
            param:
              type: string
              nullable: true
              description: The parameter that caused the error, if applicable.
            request_id:
              type: string
              description: Unique identifier for this request, useful for support.

    # ── Risk ─────────────────────────────────────────────────────────
    RiskAssessment:
      type: object
      properties:
        id:
          type: string
          description: "Unique identifier (prefixed with `risk_assessment_`)."
        object:
          type: string
          enum: [risk_assessment]
        risk_score:
          type: number
          description: The numeric risk score.
        risk_level:
          type: string
          enum: [low, medium, high]
        recommendation:
          type: string
          enum: [allow, review, block]
          description: Suggested action — advisory; your application decides.
        triggered_signals:
          type: array
          items:
            type: string
        reasons:
          type: array
          items:
            type: string
        signal_details:
          type: object
          additionalProperties: true
        created:
          type: integer
          description: Unix timestamp (seconds).

    # ── Pagination wrapper ───────────────────────────────────────────
    ListMeta:
      type: object
      required: [object, url, has_more, data]
      properties:
        object:
          type: string
          enum: [list]
        url:
          type: string
          description: The URL for this list endpoint.
        has_more:
          type: boolean
          description: Whether there are more items beyond this page.
        total_count:
          type: integer
          description: Total number of matching items (present on some endpoints).

    # ── Verification Session ─────────────────────────────────────────
    CreateVerificationSessionRequest:
      type: object
      required: [type, workflow_id]
      properties:
        type:
          $ref: "#/components/schemas/VerificationType"
        workflow_id:
          type: string
          maxLength: 64
          description: |
            ID of the published workflow to execute (wf_...). Required — the
            workflow carries the use case, target jurisdictions, verification
            methods, and preferences; compliance is resolved from it. A
            non-empty `methods` array on the request is rejected.
        captcha_token:
          type: string
          maxLength: 2048
          description: |
            Cloudflare Turnstile token. Required only when creating sessions
            with a publishable key from the browser in a live organization;
            not needed for secret keys or sandbox organizations.
        jurisdiction:
          type: string
          pattern: "^[A-Z]{2}(-[A-Z0-9]{2,3})?$"
          description: "ISO 3166-1 alpha-2, optionally with region (e.g. US-CA). Auto-detected if omitted."
        delivery_jurisdiction:
          type: string
          pattern: "^[A-Z]{2}(-[A-Z0-9]{2,3})?$"
          description: "Delivery/shipping jurisdiction for VPN mismatch detection."
        custom_user_message:
          type: string
          maxLength: 500
          description: Override the default user-facing message shown in the widget.
        client_reference_id:
          type: string
          maxLength: 255
          description: Your internal reference ID for this session.
        credential_hash:
          type: string
          maxLength: 128
          description: Pre-computed credential hash for deduplication.
        return_url:
          type: string
          format: uri
          description: URL to redirect to after verification completes.
        cancel_url:
          type: string
          format: uri
          description: URL to redirect to if the user cancels.
        metadata:
          type: object
          additionalProperties:
            type: string
          description: Arbitrary key-value metadata attached to the session.
        idempotency_key:
          type: string
          maxLength: 255
          description: Idempotency key to prevent duplicate session creation.
        skip_verification:
          type: boolean
          description: "Sandbox organizations only. Instantly creates a verified session without requiring verification; rejected in live organizations."
        email:
          type: string
          format: email
          description: Email for verified person lookup. Hashed server-side.
        phone:
          type: string
          maxLength: 20
          description: Phone for verified person lookup. Hashed server-side.
        accept_existing:
          type: boolean
          description: Whether to accept existing cross-site verifications. Default false.
        min_strength:
          $ref: "#/components/schemas/CredentialStrength"
        max_age:
          type: string
          pattern: "^\\d+d$"
          description: "Maximum age of existing verification (e.g. '30d')."
        required_methods:
          type: array
          items:
            $ref: "#/components/schemas/VerificationMethod"
          description: Require ALL of these methods to be previously verified.
        vp_token:
          type: string
          description: Reusable verified person token from a prior verification.

    ComplianceBlock:
      type: object
      properties:
        jurisdiction:
          type: string
          nullable: true
        age_tier:
          type: string
          nullable: true
        mdl_acceptance_status:
          type: string
          enum: [full, supplementary, not_recognized, unknown]
          description: |
            Whether mobile driver's licenses are legally accepted.
        mdl_legally_recognized:
          type: boolean
          description: Convenience boolean; true when mdl_acceptance_status is "full".
        physical_check_required:
          type: boolean
        user_message:
          type: string
          nullable: true
        governing_regulator:
          type: string
          nullable: true
        rule_version:
          type: string
          nullable: true

    VerificationSession:
      type: object
      properties:
        id:
          type: string
          description: "Unique identifier (prefixed with `vks_`)."
        object:
          type: string
          enum: [verification_session]
        status:
          $ref: "#/components/schemas/SessionStatus"
        type:
          $ref: "#/components/schemas/VerificationType"
        methods:
          type: array
          items:
            type: string
        current_method:
          type: string
          nullable: true
        client_reference_id:
          type: string
          nullable: true
        return_url:
          type: string
          nullable: true
        cancel_url:
          type: string
          nullable: true
        client_secret:
          type: string
          nullable: true
          description: One-time client secret for the frontend SDK. Only returned on creation.
        metadata:
          type: object
          additionalProperties:
            type: string
          nullable: true
        expires_at:
          type: integer
          description: Unix timestamp (seconds).
        completed_at:
          type: integer
          nullable: true
          description: Unix timestamp (seconds).
        cancelled_at:
          type: integer
          nullable: true
          description: Unix timestamp (seconds).
        created:
          type: integer
          description: Unix timestamp (seconds).
        updated:
          type: integer
          description: Unix timestamp (seconds).
        verification_path:
          type: string
          nullable: true
          description: How this session was verified (null for standard flow).
        age_tier:
          type: string
          nullable: true
          description: "Resolved age tier (e.g. min_age_21). Null until compliance is resolved."
        jurisdiction:
          type: string
          nullable: true
        requires_email_otp:
          type: boolean
          description: |
            True when the session's email belongs to an existing verified
            person and the workflow requires the user to prove email
            ownership via OTP before reusing credentials. Recomputed on
            every retrieve.
        active_device:
          type: string
          nullable: true
          description: |
            Desktop→mobile handoff state — "mobile" while a phone holds the
            session lock, null otherwise.
        device_locked_at:
          type: integer
          nullable: true
          description: Unix timestamp (seconds) when the device lock was taken.
        jurisdiction_audit_declared:
          type: string
          nullable: true
          description: Jurisdiction declared on the request, if any.
        jurisdiction_audit_ip_derived:
          type: string
          nullable: true
          description: Jurisdiction derived from the request IP.
        jurisdiction_audit_resolved:
          type: string
          nullable: true
          description: Jurisdiction the session actually resolved to.
        jurisdiction_audit_source:
          type: string
          nullable: true
          description: How the jurisdiction was resolved (e.g. declared, ip_derived).
        jurisdiction_audit_mismatch:
          type: boolean
          nullable: true
          description: True when the declared and IP-derived jurisdictions disagree.
        physical_check_required:
          type: boolean
        user_message:
          type: string
          nullable: true
        compliance:
          nullable: true
          oneOf:
            - $ref: "#/components/schemas/ComplianceBlock"
        results:
          type: array
          items:
            $ref: "#/components/schemas/VerificationResult"
          description: Included when expand[]=results is requested.
        ip_jurisdiction:
          type: string
          description: Detected IP-based jurisdiction (returned on creation when applicable).
        jurisdiction_mismatch:
          type: boolean
          description: Whether delivery_jurisdiction mismatches the detected IP jurisdiction.

    VerificationSessionListItem:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          enum: [verification_session]
        status:
          type: string
        type:
          type: string
        verification_path:
          type: string
          nullable: true
        created:
          type: integer

    VerificationResult:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          enum: [verification_result]
        method:
          type: string
        status:
          type: string
        confidence:
          type: number
          nullable: true
        age_estimate:
          type: integer
          nullable: true
        age_verified:
          type: boolean
          nullable: true
        identity_verified:
          type: boolean
          nullable: true
        provider_name:
          type: string
          nullable: true
        error_code:
          type: string
          nullable: true
        error_message:
          type: string
          nullable: true
        processing_time_ms:
          type: integer
          nullable: true
        created:
          type: integer

    CancelSessionResponse:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          enum: [verification_session]
        status:
          type: string
          enum: [cancelled]
        cancelled_at:
          type: integer
          nullable: true

    # ── Compliance ───────────────────────────────────────────────────
    ComplianceCheckResult:
      type: object
      properties:
        use_case:
          type: string
        jurisdiction:
          type: string
        prohibited:
          type: boolean
        prohibited_reason:
          type: string
          nullable: true
        required_age_tier:
          type: string
          nullable: true
        allowed_methods:
          type: array
          items:
            type: string
        mdl_acceptance_status:
          type: string
        governing_regulator:
          type: string
          nullable: true
        legal_citations:
          type: array
          items:
            type: string
        data_retention_days:
          type: integer
          nullable: true
        biometric_retention_days:
          type: integer
          nullable: true
        consent_required:
          type: boolean
        consent_text:
          type: string
          nullable: true
        privacy_frameworks:
          type: array
          items:
            type: string
        penalty_description:
          type: string
          nullable: true
        penalty_type:
          type: string
          nullable: true
        credential_validity_days:
          type: integer
        rule_version:
          type: string
          nullable: true

    ComplianceCheckResponse:
      type: object
      properties:
        jurisdiction:
          type: string
        jurisdiction_source:
          type: string
          enum: [explicit, auto_detected]
        results:
          type: array
          items:
            $ref: "#/components/schemas/ComplianceCheckResult"
        most_restrictive:
          type: object
          properties:
            required_age_tier:
              type: string
              nullable: true
            allowed_methods:
              type: array
              items:
                type: string
            consent_required:
              type: boolean
            data_retention_days:
              type: integer
              nullable: true
            any_prohibited:
              type: boolean
            prohibited_use_cases:
              type: array
              items:
                type: string

    # ── Verified Person ──────────────────────────────────────────────
    VerifiedPersonLookupRequest:
      type: object
      properties:
        email:
          type: string
          format: email
          description: Email address to look up.
        phone:
          type: string
          description: Phone number to look up.
        methods:
          type: array
          items:
            type: string
          description: Required verification methods.
        min_strength:
          type: string
          description: Minimum credential strength level.
        max_age:
          type: string
          description: "Maximum age of verification in days (e.g. '30')."

    VerifiedPersonLookupResponse:
      type: object
      properties:
        object:
          type: string
          enum: [verified_person_lookup]
        verified:
          type: boolean
        verified_person_id:
          type: string
          nullable: true
        credentials:
          type: array
          items:
            type: object
            properties:
              method:
                type: string
              strength:
                type: string
              verified_at:
                type: integer
                description: Unix timestamp (seconds).
              expires_at:
                type: integer
                description: Unix timestamp (seconds).

    # ── Webhook Endpoint ─────────────────────────────────────────────
    CreateWebhookEndpointRequest:
      type: object
      required: [url, enabled_events]
      properties:
        url:
          type: string
          format: uri
          description: The URL to receive webhook events.
        enabled_events:
          type: array
          items:
            type: string
          minItems: 1
          description: |
            Event types this endpoint receives (e.g.
            `verification_session.verified`, `verification_session.failed`).
        description:
          type: string
          maxLength: 500
        metadata:
          type: object
          additionalProperties:
            type: string

    UpdateWebhookEndpointRequest:
      type: object
      properties:
        url:
          type: string
          format: uri
        enabled_events:
          type: array
          items:
            type: string
          minItems: 1
        status:
          type: string
          enum: [enabled, disabled]
        description:
          type: string
          maxLength: 500
        metadata:
          type: object
          additionalProperties:
            type: string

    WebhookEndpoint:
      type: object
      properties:
        id:
          type: string
          description: "Unique identifier (prefixed with `we_`)."
        object:
          type: string
          enum: [webhook_endpoint]
        url:
          type: string
        enabled_events:
          type: array
          items:
            type: string
        status:
          type: string
          enum: [enabled, disabled]
        api_version:
          type: string
          nullable: true
        description:
          type: string
          nullable: true
        metadata:
          type: object
          additionalProperties:
            type: string
          nullable: true
        created:
          type: integer
          description: Unix timestamp (seconds).
        updated:
          type: integer
          description: Unix timestamp (seconds).
        secret:
          type: string
          description: Webhook signing secret. Only returned on creation.

    WebhookEndpointDeleted:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          enum: [webhook_endpoint]
        deleted:
          type: boolean
          enum: [true]

    # ── Event ────────────────────────────────────────────────────────
    DeliverySummary:
      type: object
      properties:
        total:
          type: integer
        delivered:
          type: integer
        failed:
          type: integer
        pending:
          type: integer

    Event:
      type: object
      properties:
        id:
          type: string
          description: "Unique identifier (prefixed with `evt_`)."
        object:
          type: string
          enum: [event]
        type:
          type: string
          description: "Event type (e.g. `verification_session.verified`)."
        api_version:
          type: string
          nullable: true
        data:
          type: object
          properties:
            object:
              description: The object that triggered the event.
        pending_webhooks:
          type: integer
          description: Number of webhooks yet to be delivered.
        created:
          type: integer
          description: Unix timestamp (seconds).
        delivery_summary:
          nullable: true
          oneOf:
            - $ref: "#/components/schemas/DeliverySummary"

    WebhookDelivery:
      type: object
      properties:
        id:
          type: string
        object:
          type: string
          enum: [webhook_delivery]
        endpoint_id:
          type: string
        event_id:
          type: string
        event_type:
          type: string
          nullable: true
        url:
          type: string
        response_status:
          type: integer
          nullable: true
        attempt:
          type: integer
        max_attempts:
          type: integer
        delivered_at:
          type: integer
          nullable: true
          description: Unix timestamp (seconds).
        failed_at:
          type: integer
          nullable: true
          description: Unix timestamp (seconds).
        next_retry_at:
          type: integer
          nullable: true
          description: Unix timestamp (seconds).
        created:
          type: integer
          description: Unix timestamp (seconds).

  parameters:
    Limit:
      name: limit
      in: query
      schema:
        type: integer
        minimum: 1
        maximum: 100
        default: 10
      description: Number of items to return per page.
    StartingAfter:
      name: starting_after
      in: query
      schema:
        type: string
      description: Cursor for forward pagination. Returns items created after this ID.
    EndingBefore:
      name: ending_before
      in: query
      schema:
        type: string
      description: Cursor for backward pagination. Returns items created before this ID.

paths:
  # ── Verification Sessions ────────────────────────────────────────
  /v1/verification_sessions:
    post:
      operationId: createVerificationSession
      summary: Create a verification session
      tags: [Verification Sessions]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateVerificationSessionRequest"
      responses:
        "201":
          description: Session created.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerificationSession"
        "400":
          description: Invalid request.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication failed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "402":
          description: Billing suspended (billing_suspended) or free test quota exhausted (test_quota_exceeded).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "422":
          description: Compliance rejection (e.g. use_case_prohibited, jurisdiction_unresolvable).
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "429":
          description: Rate limit exceeded.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

    get:
      operationId: listVerificationSessions
      summary: List verification sessions
      tags: [Verification Sessions]
      parameters:
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
        - $ref: "#/components/parameters/EndingBefore"
        - name: status
          in: query
          schema:
            $ref: "#/components/schemas/SessionStatus"
          description: Filter by session status.
      responses:
        "200":
          description: A paginated list of verification sessions.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/ListMeta"
                  - type: object
                    properties:
                      total_count:
                        type: integer
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/VerificationSessionListItem"
        "401":
          description: Authentication failed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/verification_sessions/{id}:
    get:
      operationId: getVerificationSession
      summary: Retrieve a verification session
      tags: [Verification Sessions]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: The verification session ID.
        - name: "expand[]"
          in: query
          schema:
            type: array
            items:
              type: string
              enum: [results]
          description: Fields to expand (e.g. `results`).
      responses:
        "200":
          description: The verification session.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerificationSession"
        "404":
          description: Session not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/verification_sessions/{id}/cancel:
    post:
      operationId: cancelVerificationSession
      summary: Cancel a verification session
      tags: [Verification Sessions]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: The verification session ID.
      responses:
        "200":
          description: Session cancelled.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/CancelSessionResponse"
        "404":
          description: Session not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Compliance ───────────────────────────────────────────────────
  /v1/compliance/check:
    get:
      operationId: checkCompliance
      summary: Check compliance requirements
      description: |
        Returns regulatory details for one or more use cases in a jurisdiction,
        plus a most-restrictive merged summary. No request body; all parameters
        are query strings.
      tags: [Compliance]
      parameters:
        - name: use_cases
          in: query
          required: true
          schema:
            type: string
          description: "Comma-separated use cases (e.g. `alcohol_delivery,tobacco_nicotine`). Max 10."
        - name: jurisdiction
          in: query
          schema:
            type: string
            pattern: "^[A-Z]{2}(-[A-Z0-9]{2,3})?$"
          description: "ISO 3166 code (e.g. `US-CA`). Auto-detected from IP if omitted."
      responses:
        "200":
          description: Compliance check results.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/ComplianceCheckResponse"
        "400":
          description: Invalid request.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Verified Person ──────────────────────────────────────────────
  /v1/verified_person/lookup:
    post:
      operationId: lookupVerifiedPerson
      summary: Look up a verified person
      description: |
        Server-to-server lookup. Check if a user is already verified before
        loading the SDK. Requires at least one of `email` or `phone`.
      tags: [Verified Person]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/VerifiedPersonLookupRequest"
      responses:
        "200":
          description: Lookup result.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/VerifiedPersonLookupResponse"
        "400":
          description: Missing email or phone.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication failed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Webhook Endpoints ──────────────────────────────────────────
  /v1/webhook_endpoints:
    post:
      operationId: createWebhookEndpoint
      summary: Create a webhook endpoint
      description: |
        The signing secret is returned only in this response. Store it securely.
      tags: [Webhook Endpoints]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/CreateWebhookEndpointRequest"
      responses:
        "201":
          description: Endpoint created. Includes the one-time signing secret.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpoint"
        "400":
          description: Invalid request.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

    get:
      operationId: listWebhookEndpoints
      summary: List webhook endpoints
      tags: [Webhook Endpoints]
      parameters:
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
      responses:
        "200":
          description: A paginated list of webhook endpoints.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/ListMeta"
                  - type: object
                    properties:
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/WebhookEndpoint"

  /v1/webhook_endpoints/{id}:
    get:
      operationId: getWebhookEndpoint
      summary: Retrieve a webhook endpoint
      tags: [Webhook Endpoints]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: The webhook endpoint ID.
      responses:
        "200":
          description: The webhook endpoint with delivery health stats.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/WebhookEndpoint"
                  - type: object
                    properties:
                      health:
                        type: object
                        properties:
                          total_deliveries:
                            type: integer
                          success_rate:
                            type: integer
                            nullable: true
                            description: Percentage (0-100).
                          failures_24h:
                            type: integer
                          last_delivery_at:
                            type: integer
                            nullable: true
                            description: Unix timestamp (seconds).
        "404":
          description: Endpoint not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

    post:
      operationId: updateWebhookEndpoint
      summary: Update a webhook endpoint
      tags: [Webhook Endpoints]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: The webhook endpoint ID.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: "#/components/schemas/UpdateWebhookEndpointRequest"
      responses:
        "200":
          description: The updated webhook endpoint.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpoint"
        "404":
          description: Endpoint not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

    delete:
      operationId: deleteWebhookEndpoint
      summary: Delete a webhook endpoint
      tags: [Webhook Endpoints]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: The webhook endpoint ID.
      responses:
        "200":
          description: Endpoint deleted.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/WebhookEndpointDeleted"
        "404":
          description: Endpoint not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Events ─────────────────────────────────────────────────────
  /v1/events:
    get:
      operationId: listEvents
      summary: List events
      tags: [Events]
      parameters:
        - $ref: "#/components/parameters/Limit"
        - $ref: "#/components/parameters/StartingAfter"
        - name: type
          in: query
          schema:
            type: string
          description: "Filter by event type (e.g. `verification_session.verified`)."
        - name: created_after
          in: query
          schema:
            type: integer
          description: Unix timestamp. Only return events created after this time.
        - name: created_before
          in: query
          schema:
            type: integer
          description: Unix timestamp. Only return events created before this time.
        - name: session_id
          in: query
          schema:
            type: string
          description: Filter events by verification session ID.
      responses:
        "200":
          description: A paginated list of events.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/ListMeta"
                  - type: object
                    properties:
                      total_count:
                        type: integer
                      data:
                        type: array
                        items:
                          $ref: "#/components/schemas/Event"

  /v1/events/{id}:
    get:
      operationId: getEvent
      summary: Retrieve an event
      tags: [Events]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: The event ID.
      responses:
        "200":
          description: The event with delivery details.
          content:
            application/json:
              schema:
                allOf:
                  - $ref: "#/components/schemas/Event"
                  - type: object
                    properties:
                      deliveries:
                        type: array
                        items:
                          $ref: "#/components/schemas/WebhookDelivery"
        "404":
          description: Event not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  # ── Risk ───────────────────────────────────────────────────────
  /v1/risk/score:
    post:
      operationId: scoreRisk
      summary: Score fraud risk
      description: |
        Score the fraud risk of a set of signals. At least one of `ip`,
        `email`, `phone`, or `device_id` is required. Identifiers are hashed
        server-side before anything is persisted.
      tags: [Risk]
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                ip:
                  type: string
                  description: The user's IP address (IPv4 or IPv6).
                email:
                  type: string
                  format: email
                phone:
                  type: string
                device_id:
                  type: string
                  description: A device fingerprint or stable device identifier.
                user_agent:
                  type: string
                session_id:
                  type: string
                  description: Optional verification session (vks_...) to associate.
                claimed_country:
                  type: string
                  description: "Jurisdiction the user claims (ISO 3166, e.g. US, US-CA)."
                doc_country:
                  type: string
                  description: ISO 3166 alpha-2 country of a presented document.
                doc_region:
                  type: string
                  description: 1–3 character region/state code of a presented document.
                required_jurisdiction:
                  type: string
                  description: The jurisdiction your use case requires (ISO 3166).
      responses:
        "201":
          description: The risk assessment.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RiskAssessment"
        "400":
          description: No identifier supplied.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
        "401":
          description: Authentication failed.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"

  /v1/risk/{id}:
    get:
      operationId: getRiskAssessment
      summary: Retrieve a risk assessment
      tags: [Risk]
      parameters:
        - name: id
          in: path
          required: true
          schema:
            type: string
          description: The risk assessment ID.
      responses:
        "200":
          description: The risk assessment.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/RiskAssessment"
        "404":
          description: Risk assessment not found.
          content:
            application/json:
              schema:
                $ref: "#/components/schemas/Error"
