openapi: 3.0.3
info:
  version: '1.0'
  title: Identity API
  description: Reference for Sardine Universal Identity API (identity.sardine.ai)
  contact:
    email: support@sardine.io
    name: Sardine
    url: docs.sardine.io
servers:
- url: https://api.sandbox.sardine.ai/v1
  description: Sandbox
- url: https://api.sardine.ai/v1
  description: Production

components:
  securitySchemes:
    ClientSecret:
      type: http
      scheme: basic
      description: >
        HTTP Basic Auth. Use your `clientId` as the username and `clientSecret` as the password.

  schemas:
    Address:
      type: object
      properties:
        street:
          type: string
          example: 123 Main St
        city:
          type: string
          example: San Francisco
        region:
          type: string
          description: State or province (ISO 3166-2 subdivision code)
          example: CA
        postalCode:
          type: string
          example: '94105'

    IdentityProfile:
      type: object
      properties:
        clientId:
          type: string
          description: The client this identity is associated with
          example: acme-corp
        userId:
          type: string
          description: Sardine customer ID (UUID)
          example: 3f8c1a22-1234-4abc-9def-000000000001
        consentId:
          type: string
          nullable: true
          description: Consent record ID, null if the identity belongs to this client directly
          example: b1c2d3e4-5678-4abc-9def-000000000002
        consentedAt:
          type: string
          format: date-time
          nullable: true
          description: When the user gave consent to share this identity
          example: '2026-05-01T10:30:00Z'
        revokedAt:
          type: string
          format: date-time
          nullable: true
          description: When consent was revoked, null if still active
          example: null
        primaryIdentity:
          type: boolean
          description: True if this identity was created by the calling client (no consent required)
          example: false
        clientName:
          type: string
          description: Display name of the client
          example: Acme Corp
        fullName:
          type: string
          example: Jane Smith
        dateOfBirth:
          type: string
          format: date
          example: '1990-06-15'
        emailAddress:
          type: string
          format: email
          example: jane@example.com
        phoneNumber:
          type: string
          description: E.164 formatted phone number
          example: '+14155551234'
        address:
          $ref: '#/components/schemas/Address'

    DocumentData:
      type: object
      description: Extracted data from the verified government ID document
      properties:
        documentType:
          type: string
          example: DRIVERS_LICENSE
        documentNumber:
          type: string
          example: D1234567
        dateOfBirth:
          type: string
          format: date
          example: '1990-06-15'
        expiryDate:
          type: string
          format: date
          example: '2028-06-15'
        issuingCountry:
          type: string
          description: ISO 3166-1 alpha-2 country code
          example: US
        firstName:
          type: string
          example: Jane
        lastName:
          type: string
          example: Smith
        gender:
          type: string
          example: F
        address:
          $ref: '#/components/schemas/Address'

    DocumentKyc:
      type: object
      description: Document images from the verification session
      properties:
        front:
          type: string
          description: Base64-encoded front image of the document
        back:
          type: string
          description: Base64-encoded back image of the document
        selfie:
          type: string
          description: Base64-encoded selfie image

    IdentityEntity:
      type: object
      properties:
        documentKyc:
          $ref: '#/components/schemas/DocumentKyc'
          nullable: true
          description: Document images, present only when `doc_kyc` scope was consented
        documentData:
          $ref: '#/components/schemas/DocumentData'
          nullable: true
          description: Extracted document fields, present only when `doc_kyc` scope was consented
        profile:
          $ref: '#/components/schemas/IdentityProfile'
          nullable: true
          description: Profile data, present only when `profile` scope was consented
        level:
          type: string
          description: Risk level of the user (high, medium, low). Present when risk enrichment is enabled for the client.
          example: low
          enum: [high, medium, low]
        customer:
          type: object
          description: Full risk customer object from the Sardine risk engine. Present when risk enrichment is enabled.

    ErrorResponse:
      type: object
      properties:
        error:
          type: string
          example: Customer not found for customer ID
        status:
          type: integer
          example: 400

security:
  - ClientSecret: []

paths:
  /identity/entities:
    post:
      tags: ["Customer Management"]
      summary: Create Customer
      description: >
        Creates a new identity record for a customer. The phone number must be unique
        per client. Returns the Sardine `customerId` which is used in all subsequent
        identity API calls.
      operationId: post-identity-entities
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phoneNumber]
              properties:
                phoneNumber:
                  type: string
                  description: E.164 formatted phone number
                  example: '+14155551234'
            example:
              phoneNumber: '+14155551234'
      responses:
        '200':
          description: Customer created successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  customerId:
                    type: string
                    description: Sardine-assigned customer UUID
                    example: 3f8c1a22-1234-4abc-9def-000000000001
                  createdAt:
                    type: string
                    format: date-time
                    example: '2026-05-01T10:00:00Z'
              example:
                customerId: 3f8c1a22-1234-4abc-9def-000000000001
                createdAt: '2026-05-01T10:00:00Z'
        '400':
          description: Phone number missing, invalid, or customer already exists
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid client credentials

  /identity/entities/search:
    post:
      tags: ["Customer Management"]
      summary: Search Customer
      description: >
        Looks up a customer by phone number. Use this to check whether a customer
        already exists before calling `POST /identity/entities`.
      operationId: post-identity-entities-search
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [phoneNumber]
              properties:
                phoneNumber:
                  type: string
                  description: E.164 formatted phone number
                  example: '+14155551234'
            example:
              phoneNumber: '+14155551234'
      responses:
        '200':
          description: Customer record (may be empty if not found)
          content:
            application/json:
              schema:
                type: object
                properties:
                  customerId:
                    type: string
                    nullable: true
                    example: 3f8c1a22-1234-4abc-9def-000000000001
                  createdAt:
                    type: string
                    format: date-time
                    nullable: true
        '400':
          description: Phone number is required
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid client credentials

  /identity/entities/{customerId}:
    get:
      tags: ["Customer Management"]
      summary: Get Customer Identity
      description: >
        Returns the verified identity data for a customer, including profile, document
        data, and document images. The response is gated by the user's consent:

        - `profile` fields are returned when the `profile` scope was consented
        - `documentKyc` and `documentData` are returned when the `doc_kyc` scope was consented

        If the identity belongs to another client, a valid `kyc_sharing` consent record
        (consented and not revoked) must exist.
      operationId: get-identity-entities-customerid
      parameters:
        - name: customerId
          in: path
          required: true
          schema:
            type: string
          description: Sardine customer UUID returned from `POST /identity/entities`
          example: 3f8c1a22-1234-4abc-9def-000000000001
      responses:
        '200':
          description: Identity data for the customer
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/IdentityEntity'
        '400':
          description: Consent not found, pending, or revoked
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid client credentials
        '404':
          description: Customer not found

  /identity/consents/widget:
    post:
      tags: ["Consent Widget"]
      summary: Get KYC Widget URL
      description: >
        Generates a hosted widget URL that you redirect your user to (or embed as an iframe)
        to complete KYC or share an existing verified identity.


        **Flows**

        - `kyc_input` *(default)* — The user verifies their identity from scratch in the Sardine
          hosted widget (passport, driver's license, liveness check, etc.). Use this for new users
          or when you need specific additional verification steps.

        - `kyc_sharing` — The user consents to share an identity already verified by another Sardine
          partner. This produces near-instant approval for returning Sardine users.


        **Scopes**

        Control which data the widget will collect or share:

        - `profile` — Basic personal information (name, DOB, address, email, phone)
        - `doc_kyc` — Government ID document scan + liveness check
        - `liveness` — Liveness check only
        - `ssn` — Social Security Number (US users)


        **After the Widget**

        When the user completes the flow the widget redirects to `successUrl`. Call
        `GET /identity/entities/{customerId}` to retrieve the verified data.
      operationId: post-identity-consents-widget
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              required: [customerId]
              properties:
                customerId:
                  type: string
                  description: Sardine customer UUID
                  example: 3f8c1a22-1234-4abc-9def-000000000001
                successUrl:
                  type: string
                  format: uri
                  description: URL to redirect the user to after successful completion
                  example: 'https://yourapp.com/kyc/success'
                manualKycUrl:
                  type: string
                  format: uri
                  description: Fallback URL if automated verification fails and manual review is needed
                  example: 'https://yourapp.com/kyc/manual'
                scope:
                  type: array
                  description: >
                    Verification scopes to request. Defaults to `["profile"]`.
                    Use `["profile", "doc_kyc"]` for a full document verification flow.
                  items:
                    type: string
                    enum: [profile, doc_kyc, liveness, ssn]
                  default: [profile]
                  example: [profile, doc_kyc]
                flow:
                  type: string
                  description: Verification flow type. Defaults to `kyc_input`.
                  enum: [kyc_input, kyc_sharing]
                  default: kyc_input
                  example: kyc_input
                autoRedirect:
                  type: boolean
                  description: >
                    If true, the widget automatically redirects to `successUrl` upon completion
                    without showing a confirmation screen.
                  default: false
                  example: false
            example:
              customerId: 3f8c1a22-1234-4abc-9def-000000000001
              successUrl: 'https://yourapp.com/kyc/success'
              scope: [profile, doc_kyc]
              flow: kyc_input
      responses:
        '200':
          description: Widget URL generated successfully
          content:
            application/json:
              schema:
                type: object
                properties:
                  widgetUrl:
                    type: string
                    format: uri
                    description: >
                      Fully-formed URL to the Sardine hosted KYC widget. Redirect the user
                      here or embed it in an iframe.
                    example: 'https://identity.sardine.ai/?client_token=abc123&consent_id=xyz789&success_url=...'
              example:
                widgetUrl: 'https://identity.sardine.ai/?client_token=abc123&consent_id=xyz789&success_url=https%3A%2F%2Fyourapp.com%2Fkyc%2Fsuccess'
        '400':
          description: Customer ID missing or not found, invalid scope or flow, or user already consented
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
        '401':
          description: Invalid client credentials
