openapi: 3.1.0
info:
  title: VerifiMe Partner API
  version: "1.0.0"
  description: |
    Partner-facing API for integrating customer identity verification into your onboarding flow.

    All requests must be made over HTTPS.

servers:
  - url: https://api.stage.verifime.com
    description: Staging
  - url: https://api.prod.verifime.com
    description: Production

paths:
  /v1/user/invite:
    post:
      operationId: invite
      security: []
      summary: Create a customer verification invitation
      description: |
        Creates a verification session and returns a tracking reference and invitation links.
        Send the `inviteUrl` or `qrCodeUrl` to your customer to begin the VerifiMe onboarding wizard.
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/UserInviteRequest'
      responses:
        '200':
          description: Invitation created successfully
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UserInviteResponse'
              example:
                trackingReference: 7ea40290-44b7-424e-babb-31da7ca1dcd1
                inviteUrl: https://portal.stage.verifime.com/sign-up/track-id/7ea40290-44b7-424e-babb-31da7ca1dcd1
                qrCodeUrl: https://qr-code.dev.verifime.com/qr?url=https%3A%2F%2Fportal.stage.verifime.com%2Fsign-up%2Ftrack-id%2F7ea40290-44b7-424e-babb-31da7ca1dcd1
        '400':
          description: |
            Request validation failed. Returns a `validationResults` array identifying each invalid
            field with a machine-readable error code and human-readable message.

            Malformed field values that cannot be parsed (e.g. an invalid date in `dateOfBirth`)
            return a bare `400` with no response body.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ValidationErrorResponse'
              example:
                request:
                  email: test@example.com
                  code: TESTCODE
                  referenceData: null
                  customerContext:
                    fullName: null
                    firstName: Jack test2
                    familyName: Smith
                    middleName: null
                    dateOfBirth: null
                    preferredContactNumber: null
                    address: null
                  sendInvitationEmail: false
                validationResults:
                  - path: ""
                    name: code
                    resultType: Error
                    resultName: InvalidOrganisationCodeLength
                    resultScope: Field
                    message: Supplied organisation code is not the correct length
                  - path: customerContext
                    name: firstName
                    resultType: Error
                    resultName: PersonNameInvalidCharacter
                    resultScope: Field
                    message: A person name is not allowed to have the character at position 9

  /v1/user/signup/tracking/{trackingReference}/status:
    get:
      operationId: tracking-status
      security: []
      summary: Get tracking reference status
      description: |
        Returns the current status of a customer verification session.
        Poll this endpoint to determine whether the customer has completed data entry.
      parameters:
        - name: trackingReference
          in: path
          required: true
          description: The tracking reference UUID returned from the invite endpoint
          schema:
            type: string
            format: uuid
      responses:
        '200':
          description: Current session status
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TrackingReferenceStatusResponse'
        '400':
          description: Invalid tracking reference

  /test/tracking/{trackingReference}/verify:
    post:
      operationId: force-verify
      servers:
        - url: https://verify-entity.stage.verifime.com
          description: Staging
      security: []
      summary: Force verify a tracking reference
      description: |
        Immediately completes verification for a given tracking reference, bypassing normal Phase 2 processing.

        Use this when Phase 2 stalls in staging and the `RISK_ASSESSMENT_UPDATED` webhook does not arrive after 5 minutes.
        This endpoint is available in staging only.

        **Note:** This permanently advances verification state. Use a fresh tracking reference for each test run.
      parameters:
        - name: trackingReference
          in: path
          required: true
          description: The tracking reference UUID returned by the invite endpoint
          schema:
            type: string
            format: uuid
      responses:
        '204':
          description: Verification advanced. The `RISK_ASSESSMENT_UPDATED` webhook will follow shortly.
        '404':
          description: Tracking reference does not exist.
        '500':
          description: Unexpected error. May occur if called before the customer has completed data entry.

webhooks:
  RISK_ASSESSMENT_UPDATED:
    post:
      operationId: risk-assessment-updated
      summary: Risk assessment updated
      description: |
        VerifiMe sends a POST request to your configured HTTPS endpoint whenever a risk assessment
        status changes. Register your endpoint via the [Webhooks](/docs/webhooks/setup) page in the developer portal.

        **Signature verification:** Each request includes an `X-VerifiMe-Signature` header containing
        the HMAC-SHA256 hex digest of the raw request body, signed with your webhook secret.

        To verify:
        1. Read the raw request body before parsing JSON
        2. Compute HMAC-SHA256 of the raw body using your shared secret
        3. Compare your digest to the `X-VerifiMe-Signature` header value using constant-time comparison
        4. Return `401` if the signatures do not match. This permanently stops delivery for the event.

        **Idempotency:** Use `eventId` to deduplicate events. VerifiMe may deliver the same event
        more than once during retries.

        **Processing:** Only process events where `assessmentStatus` is `ASSESSED`.
        Ignore `PENDING` and `PRELIMINARY` events.

        **Timeout:** Respond within 30 seconds. Requests that exceed this are treated as timeouts
        and trigger the retry path.

        **Retry behaviour:**

        | Response | VerifiMe action |
        |----------|----------------|
        | `2xx` | Accepted - no retry |
        | `401` | Invalid signature - delivery permanently stopped |
        | `408` / `5xx` / timeout | Retried with exponential backoff (15-minute interval, max 5 attempts, then dead-letter queue) |
        | `429` / `503` + `Retry-After` | Retried after the specified delay (max 12 hours) |
        | `429` / `503` without `Retry-After` | Retried with exponential backoff |
        | Other `4xx` (except `408`, `429`) | Permanent failure - no retry |
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/RiskAssessmentEvent'
      responses:
        '200':
          description: Accepted - no retry
        '401':
          description: Invalid HMAC signature - delivery permanently stopped for this event
        '429':
          description: Rate limited
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retry (max 12 hours). If omitted, VerifiMe retries with exponential backoff.
        '503':
          description: Service unavailable
          headers:
            Retry-After:
              schema:
                type: integer
              description: Seconds to wait before retry (max 12 hours). If omitted, VerifiMe retries with exponential backoff.

components:
  schemas:
    UserInviteRequest:
      type: object
      required:
        - email
        - code
      properties:
        email:
          type: string
          format: email
          description: Customer email address
        code:
          type: string
          description: Organisation code
        referenceData:
          type: string
          description: |
            Arbitrary string returned in the webhook `metadata` field.
            Use this to correlate the webhook event with your internal record (for example, an application ID).
        customerContext:
          $ref: '#/components/schemas/CustomerContext'
        sendInvitationEmail:
          type: boolean
          default: false
          description: |
            When `true`, VerifiMe sends a branded invitation email to the customer.
            When `false` (default), you are responsible for delivering the `inviteUrl` or `qrCodeUrl` to the customer.

    CustomerContext:
      type: object
      description: |
        Invitation context for this verification session. Personal detail fields pre-fill the
        onboarding wizard and personalise the invitation email. All personal detail fields are
        optional suggestions; the customer can change them during onboarding.

        **Once-consumable:** Pre-fill values are applied on the first load of the onboarding wizard only.
        If the customer revisits the invitation link, the wizard starts fresh without pre-filled values.
        `callbackUrl` is a post-completion routing field and is not subject to this constraint.

        **Privacy:** VerifiMe does not return customer document numbers or other sensitive identity data
        to partners via the API or webhook.

        **Validation:** Name fields accept Latin letters, spaces, apostrophes, and hyphens only.
        Invalid values will return a `400` response.
      properties:
        fullName:
          type: string
          description: Full name. Latin letters, spaces, apostrophes, and hyphens only.
        firstName:
          type: string
          description: First name. Latin letters, spaces, apostrophes, and hyphens only.
        familyName:
          type: string
          description: Family name. Latin letters, spaces, apostrophes, and hyphens only.
        middleName:
          type: string
          description: Middle name. Latin letters, spaces, apostrophes, and hyphens only.
        dateOfBirth:
          type: string
          format: date
          description: Date of birth in ISO 8601 format (YYYY-MM-DD)
        preferredContactNumber:
          type: string
          description: Phone number
        address:
          type: string
          description: Free-text address used for pre-fill resolution
        callbackUrl:
          type: string
          format: uri
          description: |
            Per-invite redirect URL. After the customer completes onboarding,
            the VerifiMe confirmation screen shows a "Return to [your company]"
            button that navigates to this URL.

            Overrides the organisation code's static return URL for this invite only.
            Must be an `https://` URL; other schemes are silently discarded.
            The URL's origin (scheme + hostname + port) must be registered as an
            allowed redirect origin on your organisation code. If the origin is not
            registered, this URL is not used and the organisation code's static
            return URL is used instead.
          example: https://partner.com/onboard/done?appId=xyz

    UserInviteResponse:
      type: object
      properties:
        trackingReference:
          type: string
          format: uuid
          description: |
            Unique identifier for this verification session.
            Store this value to poll the tracking status endpoint or correlate with webhook events.
        inviteUrl:
          type: string
          format: uri
          description: Direct link to the VerifiMe customer portal for this session
        qrCodeUrl:
          type: string
          format: uri
          description: QR code image URL that encodes the `inviteUrl`. Display this to customers who prefer to scan a QR code.

    TrackingReferenceStatusResponse:
      type: object
      properties:
        status:
          type: string
          enum:
            - Inactive
            - Active
            - Completed
          description: |
            Current status of the tracking reference.
            - `Inactive`: invitation created but customer has not yet started
            - `Active`: customer is currently progressing through the onboarding wizard
            - `Completed`: customer has completed data entry

    RiskAssessmentEvent:
      type: object
      required:
        - eventId
        - eventName
        - customerName
        - assessmentStatus
        - riskLevel
        - assessmentPortalUrl
        - eventTimestamp
      properties:
        eventId:
          type: string
          format: uuid
          description: Unique event identifier. Use this to deduplicate events delivered more than once during retries.
        eventName:
          type: string
          enum:
            - RISK_ASSESSMENT_UPDATED
          description: Always `RISK_ASSESSMENT_UPDATED`
        customerName:
          type: string
          description: Verified entity name (person, company, trust, or fund)
        assessmentStatus:
          type: string
          enum:
            - PENDING
            - PRELIMINARY
            - ASSESSED
          description: |
            Current state of the assessment.
            - `PENDING`: assessment queued but not yet started - ignore this event
            - `PRELIMINARY`: partial assessment in progress - ignore this event
            - `ASSESSED`: assessment complete, risk level calculated - process this event
        riskLevel:
          type: string
          enum:
            - UNKNOWN
            - LOW
            - MEDIUM
            - HIGH
            - EXTREME
          description: |
            Calculated risk level. `UNKNOWN` when `assessmentStatus` is not `ASSESSED`.
            - `UNKNOWN`: risk level not yet determined
            - `LOW`: minimal risk detected based on assessment rules
            - `MEDIUM`: moderate risk factors present
            - `HIGH`: significant risk factors identified
            - `EXTREME`: maximum risk level - multiple serious risk factors
        assessmentPortalUrl:
          type: string
          format: uri
          description: URL to the full assessment in the VerifiMe client portal
        eventTimestamp:
          type: string
          format: date-time
          description: ISO 8601 timestamp of when the event was generated
        metadata:
          type: string
          description: The `referenceData` value from the original invite call. Empty string if no `referenceData` was provided.

    ValidationResult:
      type: object
      properties:
        path:
          type:
            - string
            - "null"
          description: JSON path to the invalid field. Null when the field is at the top level of its parent object.
        name:
          type: string
          description: Name of the field that failed validation (e.g. `firstName`, `email`).
        resultType:
          type: string
          description: Severity of the result. Always `Error` for 400 responses.
        resultName:
          type: string
          description: |
            Machine-readable error code. Use this for programmatic error handling.
            Known values include `PersonNameInvalidCharacter`, `DoubleSpace`, `InvalidEmail`,
            `InvalidOrganisationCode`, `InvalidOrganisationCodeLength`, `InvalidOrganisationCodeCharacters`.
        resultScope:
          type: string
          description: Scope of the validation result (e.g. `Field`).
        message:
          type: string
          description: Human-readable description of the failure (e.g. "Name contains invalid character at position 9").

    ValidationErrorResponse:
      type: object
      description: |
        Returned when one or more fields fail validation rules (e.g. invalid characters in a name field,
        malformed email, unrecognised organisation code).
      properties:
        request:
          type: object
          description: Echo of the original request body that failed validation.
        validationResults:
          type: array
          items:
            $ref: '#/components/schemas/ValidationResult'
          description: One entry per validation failure found in the request.

