openapi: 3.0.3
info:
  title: BeTrust KYC API
  version: '6.0'
  description: >
    API de **Verificación de Identidad Biométrica (KYC)** de BeTrust.


    Flujo de integración mínimo (3 llamadas):

    1. **Generar Access Token** (OAuth2 `client_credentials`).

    2. **Crear transacción** de verificación.

    3. **Entregar el link** de verificación al usuario y **consultar el estado**
    de la transacción.


    Además, `credential_information` permite recuperar los datos del documento
    al finalizar.


    > **Ambientes:** **Sandbox (`-tst`)** y **Producción**. Los hosts de
    producción se entregan en el

    > onboarding. No hay ambiente de QA expuesto al integrador.
  contact:
    name: BeTrust
    email: felipe.gonzalez@betrust.com
servers:
  - url: https://api-tst.trust.lat/transaction-engine
    description: API de transacciones — Sandbox (TST)
tags:
  - name: Auth
    description: Autenticación OAuth2
  - name: Transacciones
    description: Creación y consulta de transacciones KYC
  - name: Credenciales
    description: Datos del documento verificado
paths:
  /oauth/token/:
    servers:
      - url: https://atenea-tst.trust.lat
        description: Autenticación (OAuth2) — Sandbox (TST)
    post:
      tags:
        - Auth
      operationId: generateAccessToken
      summary: Generar Access Token
      description: |
        Emite un token Bearer mediante **OAuth2 `client_credentials`**.
        El `client_id`/`client_secret` los entrega BeTrust por ambiente.
      security: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/TokenRequest'
            example:
              client_id: <tu_client_id>
              client_secret: <tu_client_secret>
              grant_type: client_credentials
      responses:
        '200':
          description: Token emitido
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TokenResponse'
              example:
                access_token: <access_token>
                token_type: Bearer
                expires_in: 7200
                scope: ''
                created_at: 1781823641
        '401':
          description: Credenciales inválidas
  /v1/transactions/:
    post:
      tags:
        - Transacciones
      operationId: createTransaction
      summary: Crear transacción KYC
      description: >
        Crea una transacción de verificación de identidad.


        **Scope requerido:** `engine.transaction.w`


        Todas las reglas de negocio del flujo se configuran aquí (vía
        `metadata`): tipo de liveness,

        verificación de documento, enrolamiento, antifraude, branding, etc.


        **Idempotencia.** El mecanismo es el `transaction_id` **único por
        `company_id`**: reintentar la

        creación con el mismo par devuelve `409 Duplicated` (no se crea otra
        transacción).
      security:
        - bearerAuth: []
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateTransactionRequest'
            example:
              transaction_id: order-12345
              company_id: '201'
              authorization_method: kyc_register
              tries: 3
              expiration: 600
              webhook: https://tu-dominio.cl/webhooks/kyc
              metadata:
                country: CHILE
                nin: 11111111-1
                challenges_required: 1
                capture_document_expiration: 120
                capture_facial_expiration: 120
                capture_challenge_expiration: 120
                require_document_validation: true
                document_verification: false
                verify_image_authenticity: true
                enrolment: false
                fraud_check: true
                internal_credential_check: false
      responses:
        '201':
          description: Transacción creada
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionEnvelope'
        '400':
          description: >-
            `tries` inválido, tiempos de captura mayores a la expiración, u
            otros errores.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '401':
          description: Token inválido o expirado
        '403':
          description: Scope insuficiente
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
        '409':
          description: Ya existe una transacción con ese `transaction_id` + `company_id`
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              example:
                code: 409
                resource: Transaction
                message: Duplicated
        '422':
          description: Errores de validación del modelo
      callbacks:
        webhook:
          '{$request.body#/webhook}':
            post:
              security: []
              summary: Notificación de cambio de estado (webhook)
              description: >
                Si se configura `webhook`, BeTrust hace `POST` a esa URL ante
                cambios de estado.

                El envío es **asíncrono con disparo inmediato** (no encolado);
                puede ofrecerse como

                **sync o async según configuración**. Latencia típica
                sub-segundo; como supuesto de diseño,

                tolerar hasta ~2–3 s (SLA p95 a comprometer una vez medido).


                **Idempotencia y orden.** Diseña el handler como **idempotente**
                (procesa una sola vez por

                `uuid` + estado) y **no asumas orden de llegada**. Ante duda del
                estado real, consulta

                `GET /v1/transactions/{transaction_uuid}` como **fuente
                autoritativa**.


                **Reintentos.** BeTrust **reintenta solo ante fallos de
                conexión**. Si el receptor responde

                **5xx**, la entrega **no se reintenta** (no se encola) pero el
                fallo se **registra y alerta**

                (monitoreo interno). Responde `2xx` y procesa asíncrono.


                **Firma (roadmap).** Hoy el webhook **no incluye firma**;
                BeTrust puede incorporar

                verificación HMAC sin afectar a clientes ya integrados.


                **Evitar carreras en el handoff desktop→móvil:** gatear el
                avance del canal de origen en el

                estado autoritativo (webhook recibido o polling de `GET
                .../transactions/{uuid}` cada ~1–2 s),

                no en el "listo" del móvil.
              requestBody:
                content:
                  application/json:
                    schema:
                      $ref: '#/components/schemas/WebhookPayload'
              responses:
                '200':
                  description: >-
                    Recibido por el integrador (responder 2xx; ver política de
                    reintentos en la guía de Webhooks)
  /v1/transactions/{transaction_uuid}:
    get:
      tags:
        - Transacciones
      operationId: getTransaction
      summary: Consultar transacción (estado)
      description: >
        Obtiene una transacción por su identificador.


        **Scope requerido:** `engine.transaction.r`


        El estado se interpreta con la combinación `active` / `complete` /
        `failed`

        (ver schema `Transaction`). El detalle del paso y del rechazo viene en
        `step` /

        `issue_description` (ver `StepCode` y `IssueCode`).
      security:
        - bearerAuth: []
      parameters:
        - name: transaction_uuid
          in: path
          required: true
          description: Acepta tanto el `uuid` como el `nanoid` de la transacción.
          schema:
            type: string
      responses:
        '200':
          description: Transacción encontrada
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/TransactionEnvelope'
        '401':
          description: Token inválido o expirado
        '403':
          description: Scope insuficiente o la transacción pertenece a otra compañía
        '404':
          description: No existe transacción con ese identificador
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorEnvelope'
              example:
                code: 404
                resource: Transaction
                message: Not found
        '409':
          description: Más de una transacción para el mismo identificador (duplicado)
  /heimdall/api/v1/user/credential_information:
    servers:
      - url: https://api-tst.trust.lat
        description: Datos del documento — Sandbox (TST)
    get:
      tags:
        - Credenciales
      operationId: getCredentialInformation
      summary: Datos del documento verificado
      description: |
        Recupera los datos del documento de una transacción finalizada.
        Host: `https://api-tst.trust.lat`.
      security:
        - bearerAuth: []
      parameters:
        - name: transaction_id
          in: query
          required: true
          description: UUID de la transacción.
          schema:
            type: string
      responses:
        '200':
          description: Datos de la credencial
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CredentialInformationEnvelope'
components:
  securitySchemes:
    bearerAuth:
      type: http
      scheme: bearer
      bearerFormat: JWT
  schemas:
    TokenRequest:
      type: object
      required:
        - client_id
        - client_secret
        - grant_type
      properties:
        client_id:
          type: string
        client_secret:
          type: string
        grant_type:
          type: string
          enum:
            - client_credentials
    TokenResponse:
      type: object
      properties:
        access_token:
          type: string
        token_type:
          type: string
          example: Bearer
        expires_in:
          type: integer
          example: 7200
          description: Vida del token en segundos
        scope:
          type: string
        created_at:
          type: integer
          description: Epoch de emisión
    CreateTransactionRequest:
      type: object
      required:
        - transaction_id
        - company_id
        - authorization_method
      properties:
        transaction_id:
          type: string
          description: >-
            Identificador de la transacción del lado del integrador. Único por
            `company_id`.
        company_id:
          type: string
        authorization_method:
          type: string
          description: Método de validación.
          enum:
            - kyc_register
            - kyc_register_simple
            - sms
            - email
            - fido2
            - bioidentity
          example: kyc_register
        tries:
          type: integer
          minimum: 1
          default: 3
          description: >-
            Intentos permitidos (> 0). Si se omite, se completa según
            `authorization_method`.
        expiration:
          type: integer
          default: 600
          description: Expiración del link en segundos.
        webhook:
          type: string
          format: uri
          description: URL de notificación de cambios de estado.
        scope:
          type: string
        user_id:
          type: string
        client_id:
          type: string
        creator_user_id:
          type: string
        allowed_clients:
          type: array
          items:
            type: string
        nanoid:
          type: boolean
          description: Si es true, genera un nanoid de 6 caracteres.
        user_email:
          type: string
          format: email
        link:
          type: string
          description: >-
            Plantilla de link. Soporta placeholders `{{transaction_uuid}}` y
            `{{nin}}`.
        metadata:
          $ref: '#/components/schemas/Metadata'
    Metadata:
      type: object
      description: >
        Configuración del flujo. Requerida para ciertos `authorization_method`
        (p. ej. `kyc_register`

        requiere `nin`). Nota: los **umbrales de score** (match facial, prueba
        de vida, calidad de selfie,

        antifraude) **no se configuran aquí**; se ajustan a **nivel de
        compañía** y sus valores se definen

        con cada cliente.
      properties:
        country:
          type: string
          example: CHILE
        nin:
          type: string
          description: RUT a verificar.
        challenges_required:
          type: integer
          minimum: 0
          maximum: 2
          default: 0
          description: Nº de desafíos de prueba de vida activa (máx. 2).
        document_verification:
          type: boolean
          default: false
          description: >-
            Verifica la vigencia del documento contra el regulador (Registro
            Civil).
        require_document_validation:
          type: boolean
          default: false
          description: Interrumpe el flujo si el documento no es válido.
        verify_image_authenticity:
          type: boolean
          default: false
          description: Detecta fotocopia/adulteración del documento.
        enrolment:
          type: boolean
          default: false
          description: >-
            Enrola el rostro/documento en base privada. En verificaciones
            posteriores (dentro de la vigencia) permite omitir la captura de
            documento.
        fraud_check:
          type: boolean
          default: false
          description: Valida el rostro contra la lista negra biométrica.
        internal_credential_check:
          type: boolean
          default: false
          description: >-
            Verificación interna extra si el Registro Civil reporta un valor
            incorrecto.
        capture_document_expiration:
          type: integer
          default: 120
          description: Segundos para la captura del documento.
        capture_facial_expiration:
          type: integer
          default: 120
          description: Segundos para la captura facial.
        capture_challenge_expiration:
          type: integer
          default: 120
          description: Segundos por desafío de liveness.
        document_tolerance:
          type: integer
          default: 0
          description: Tolerancia en la comparación anverso/reverso.
        webhook_full_update:
          type: boolean
          default: false
          description: Dispara webhook en cada paso (no solo al cambio de estado final).
        redirect_uri:
          type: string
          format: uri
          description: URL de redirección al finalizar (se añade ?id=<transaction_id>).
        web_redirect:
          type: boolean
          default: false
          description: Habilita la redirección web.
        timer:
          type: boolean
          default: false
          description: >-
            Tiempo de sesión como cronómetro (vs. decremento al no detectar
            DNI).
        show_timer:
          type: boolean
          default: true
          description: Muestra/oculta el timer en la UI.
        show_auto_button:
          type: boolean
          default: true
          description: Muestra el botón de captura automática en la primera vista.
    Transaction:
      type: object
      properties:
        id:
          type: integer
        uuid:
          type: string
          format: uuid
        transaction_id:
          type: string
        user_id:
          type: string
          nullable: true
        company_id:
          type: string
        authorization_method:
          type: string
        code:
          type: string
          nullable: true
        active:
          type: boolean
          description: Transacción en curso (aún no finalizada).
        complete:
          type: boolean
          description: Verificación exitosa.
        failed:
          type: boolean
          description: Verificación fallida (identidad no confirmada).
        created_at:
          type: string
          format: date-time
        updated_at:
          type: string
          format: date-time
        deleted_at:
          type: string
          format: date-time
          nullable: true
        scope:
          type: string
          nullable: true
        expiration:
          type: integer
        tries:
          type: integer
        allowed_clients:
          type: array
          items:
            type: string
          nullable: true
        nanoid:
          type: string
          nullable: true
        validator_url:
          type: string
          nullable: true
        metadata:
          $ref: '#/components/schemas/Metadata'
        client_id:
          type: string
          nullable: true
        webhook:
          type: string
          nullable: true
        verification_result:
          $ref: '#/components/schemas/VerificationResult'
        issue_description:
          type: string
          nullable: true
          description: >-
            Detalle del resultado/rechazo (ver catálogo IssueCode). Null
            mientras la transacción no finaliza.
        step:
          $ref: '#/components/schemas/StepCode'
        description:
          type: string
          nullable: true
        creator_user_id:
          type: string
          nullable: true
        extra_check_details:
          type: object
          nullable: true
      description: |
        **Interpretación del estado** (active / complete / failed):
        - `false / true  / false` → ✅ Exitosa
        - `false / false / true ` → ❌ Fallida (identidad no confirmada)
        - `true  / false / false` → ⏳ Pendiente / en curso
        - `false / false / false` → 🕒 Caducada (expiró el TTL)
    VerificationResult:
      type: object
      description: Resultado biométrico (presente al finalizar).
      properties:
        selfie_document_match:
          type: number
          format: float
          description: Score de comparación facial selfie ↔ foto del documento.
          example: 96.052
    StepCode:
      type: string
      description: Paso del flujo en el que se encuentra/termina la transacción.
      enum:
        - Session_start
        - Capture_credential_Ok
        - Capture_credential_fail
        - Session_complete_Ok
        - Session_complete_fail
        - time_out
    IssueCode:
      type: string
      description: >
        Código y detalle de resultado (`issue_description`). Catálogo:

        - **001** — Kyc Process Started With Transaction_id (Session_start)

        - **002** — The document data has been received satisfactorily
        (Capture_credential_Ok)

        - **003** — The NIN does not match the NIN on the back of the document

        - **004** — Document number information could not be obtained

        - **005** — The user NIN does not match the document NIN

        - **006** — The document is blocked or invalid

        - **007** — Cannot read information from these documents

        - **008** — Could not get faces from document

        - **009** — Cannot obtain the minimum field

        - **010** — Could not extract all the information from the document

        - **011** — Invalid document. Likely a photocopy or scan

        - **012** — The minimum score has been reached (Session_complete_Ok)

        - **013** — Flow completed successfully (Session_complete_Ok)

        - **014** — Selfie and Document photo do not match
        (Session_complete_fail)

        - **015** — The minimum score has not been reached
        (Session_complete_fail)

        - **016** — Flow completed unsuccessfully (Session_complete_fail)

        - **time_out** — Caducidad por inactividad en un paso `[POR CONFIRMAR]`
    TransactionEnvelope:
      type: object
      properties:
        code:
          type: integer
          example: 200
        resource:
          type: string
          example: Transaction
        data:
          $ref: '#/components/schemas/Transaction'
        message:
          type: string
          example: Found
    CredentialInformation:
      type: object
      properties:
        name:
          type: string
        last_name:
          type: string
        date_birth:
          type: string
          example: dd/mm/yyyy
        nin:
          type: string
        document_number:
          type: string
        identity_verification_score:
          type: number
          format: float
          example: 99.997
    CredentialInformationEnvelope:
      type: object
      properties:
        code:
          type: integer
          example: 200
        resource:
          type: string
          example: User Credential
        data:
          $ref: '#/components/schemas/CredentialInformation'
        message:
          type: string
          example: Ok
    WebhookPayload:
      type: object
      properties:
        uuid:
          type: string
          format: uuid
        transaction_id:
          type: string
        company_id:
          type: string
        authorization_method:
          type: string
        active:
          type: boolean
        complete:
          type: boolean
        failed:
          type: boolean
        updated_at:
          type: string
          format: date-time
        verification_result:
          $ref: '#/components/schemas/VerificationResult'
    ErrorEnvelope:
      type: object
      properties:
        code:
          type: integer
        resource:
          type: string
        message:
          type: string
        errors:
          type: string
          nullable: true
