> ## Documentation Index
> Fetch the complete documentation index at: https://laas.mippo.io/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# LaaS KYC onboarding models and transaction flows

> Compare Mippo LaaS KYC/AML models — Direct Onboard, Client via Partner, and Shared Onboard — with sequence diagrams and Pix settlement flows.

Mippo LaaS supports three KYC/AML ownership models. Choose the one that matches your existing compliance stack. The transaction settlement flow (Pix → stablecoin) is identical across all three; only the KYC step differs.

<Note>
  All sequence diagrams below show the **SDK method calls** on the left and the **underlying API endpoints** on the right. The SDK is a thin wrapper — you can call the REST API directly if you prefer.
</Note>

***

## Model 1 — Direct Onboard

Mippo owns the full KYC/AML flow. Your app collects CPF, selfie, and wallet address; Mippo handles everything else. Best for partners launching without an existing KYC vendor.

**KYC responsibility:** Mippo\
**Liability:** Zero partner liability

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor User
    participant App as Partner App / Backend
    participant SDK as Mippo SDK
    participant API as Mippo API
    participant Sumsub
    participant BCB as BCB / Pix Network
    participant Wallet as User Wallet

    User->>App: Initiates Pix → stablecoin purchase
    App->>SDK: mippo.kyc.submit({ cpf, biometricHash, personType })
    SDK->>API: POST /v1/laas/kyc/submit
    API->>Sumsub: Biometric + document verification
    Sumsub-->>API: Verification result
    API->>API: Sanctions screening (OFAC / COAF / UN)
    API->>API: PEP classification
    API-->>SDK: { token, riskTier: "STANDARD" | "HIGH" | "PEP" }
    SDK-->>App: kycToken, riskTier

    App->>SDK: mippo.transactions.initiate({ kycToken, amountBrl, asset, walletAddress })
    SDK->>API: POST /v1/laas/transactions/initiate
    API-->>SDK: { transactionId, authChallenge }
    SDK-->>App: authChallenge

    alt riskTier = STANDARD → MFA (TOTP)
        App->>User: Prompt for TOTP code
        User->>App: Enters TOTP code
        App->>SDK: mippo.transactions.confirmMfa({ transactionId, totpCode })
        SDK->>API: POST /v1/laas/transactions/:id/mfa
        API-->>SDK: { pixPayload, pixQrCode }
        SDK-->>App: pixPayload
    else riskTier = HIGH or PEP → Liveness check
        App->>User: Redirect to authChallenge.livenessUrl
        User->>API: Completes liveness (Sumsub WebSDK)
        API-->>App: transaction.mfa_cleared webhook
        App->>SDK: mippo.transactions.status(transactionId)
        SDK->>API: GET /v1/laas/transactions/:id/status
        API-->>SDK: { status: "AWAITING_PIX", pixPayload }
        SDK-->>App: pixPayload
    end

    App->>User: Render Pix QR code / Copy-Paste string
    User->>BCB: Pays via Pix from any Brazilian bank
    BCB-->>API: Payment confirmed
    API->>API: KYT on-chain risk check on walletAddress
    API->>Wallet: Route stablecoin (USDT / USDC)
    API->>App: POST webhook: transaction.completed { txHash, amount, walletAddress }
    App->>User: Show confirmation
```

<Tip>
  In sandbox, use CPF `000.000.001-91` to force `riskTier: "HIGH"` and exercise the liveness path.
</Tip>

***

## Model 2 — Client via Partner

Your platform already runs KYC using your own vendor (e.g. Idwall, unico check, or Sumsub directly). Mippo accepts your KYC token, adds independent sanctions screening and KYT, then issues its own `kycToken` to proceed.

**KYC responsibility:** Partner (primary) + Mippo (independent screen)\
**Liability:** Shared — your vendor clears the user; Mippo clears the transaction

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor User
    participant App as Partner App / Backend
    participant PartnerKYC as Partner KYC Vendor
    participant SDK as Mippo SDK
    participant API as Mippo API
    participant BCB as BCB / Pix Network
    participant Wallet as User Wallet

    User->>App: Completes KYC in partner platform
    App->>PartnerKYC: Standard KYC flow (CPF, document, liveness)
    PartnerKYC-->>App: partnerKycToken (vendor-issued)

    Note over App,API: Partner passes their KYC token to Mippo — no biometric re-submission needed

    App->>SDK: mippo.kyc.submit({ partnerKycToken, cpf, personType })
    SDK->>API: POST /v1/laas/kyc/submit
    API->>API: Validate partnerKycToken signature
    API->>API: Independent sanctions check (OFAC / COAF / UN)
    API->>API: PEP classification
    API-->>SDK: { token, riskTier }
    SDK-->>App: kycToken, riskTier

    App->>SDK: mippo.transactions.initiate({ kycToken, amountBrl, asset, walletAddress })
    SDK->>API: POST /v1/laas/transactions/initiate
    API-->>SDK: { transactionId, authChallenge }
    SDK-->>App: authChallenge

    alt riskTier = STANDARD → MFA (TOTP)
        App->>User: Prompt for TOTP code
        User->>App: Enters TOTP code
        App->>SDK: mippo.transactions.confirmMfa({ transactionId, totpCode })
        SDK->>API: POST /v1/laas/transactions/:id/mfa
        API-->>SDK: { pixPayload }
        SDK-->>App: pixPayload
    else riskTier = HIGH or PEP → Liveness (Mippo-owned)
        App->>User: Redirect to authChallenge.livenessUrl
        User->>API: Completes Mippo liveness check
        API-->>App: transaction.mfa_cleared webhook
        App->>SDK: mippo.transactions.status(transactionId)
        SDK->>API: GET /v1/laas/transactions/:id/status
        API-->>SDK: { pixPayload }
        SDK-->>App: pixPayload
    end

    App->>User: Render Pix QR code
    User->>BCB: Pays via Pix
    BCB-->>API: Payment confirmed
    API->>API: KYT wallet risk check
    API->>Wallet: Route stablecoin
    API->>App: POST webhook: transaction.completed
    App->>User: Show confirmation
```

<Warning>
  A `partnerKycToken` does **not** bypass Mippo's independent screening. If Mippo detects a sanctions hit that your vendor missed, `kyc.submit` returns `KYC_REJECTED` with `reason: "SANCTIONS_HIT"`. See [Compliance Notes](/docs/guides/compliance) for details.
</Warning>

***

## Model 3 — Shared Onboard

Both layers contribute to KYC. Your app collects CPF and basic data; Mippo collects the biometric (via the Sumsub WebSDK embedded in your interface). A cross-check of both data sets produces the joint KYC result.

**KYC responsibility:** Partner (CPF + document) + Mippo (biometric + liveness)\
**Liability:** Joint — both parties contribute to and share the verification outcome

```mermaid theme={null}
sequenceDiagram
    autonumber
    actor User
    participant App as Partner App / Backend
    participant SumsubWidget as Sumsub WebSDK (embedded)
    participant SDK as Mippo SDK
    participant API as Mippo API
    participant BCB as BCB / Pix Network
    participant Wallet as User Wallet

    User->>App: Starts onboarding
    App->>User: Collect CPF + document (partner-owned flow)
    User-->>App: CPF, document scan

    Note over App,SumsubWidget: Partner embeds Sumsub WebSDK for the biometric step only

    App->>SumsubWidget: Launch liveness / selfie capture
    User->>SumsubWidget: Completes biometric capture
    SumsubWidget-->>App: biometricHash (from Sumsub callback)

    App->>SDK: mippo.kyc.submit({ cpf, biometricHash, personType, mode: "shared" })
    SDK->>API: POST /v1/laas/kyc/submit
    API->>API: Cross-check CPF (partner data) vs biometric (Mippo/Sumsub data)
    API->>API: Sanctions screening (OFAC / COAF / UN)
    API->>API: PEP classification
    API-->>SDK: { token, riskTier }
    SDK-->>App: kycToken, riskTier

    App->>SDK: mippo.transactions.initiate({ kycToken, amountBrl, asset, walletAddress })
    SDK->>API: POST /v1/laas/transactions/initiate
    API-->>SDK: { transactionId, authChallenge }
    SDK-->>App: authChallenge

    alt riskTier = STANDARD → MFA (TOTP)
        App->>User: Prompt for TOTP code
        User->>App: Enters TOTP code
        App->>SDK: mippo.transactions.confirmMfa({ transactionId, totpCode })
        SDK->>API: POST /v1/laas/transactions/:id/mfa
        API-->>SDK: { pixPayload }
        SDK-->>App: pixPayload
    else riskTier = HIGH or PEP → Additional liveness required
        App->>User: Redirect to authChallenge.livenessUrl
        User->>API: Completes additional liveness check
        API-->>App: transaction.mfa_cleared webhook
        App->>SDK: mippo.transactions.status(transactionId)
        SDK->>API: GET /v1/laas/transactions/:id/status
        API-->>SDK: { pixPayload }
        SDK-->>App: pixPayload
    end

    App->>User: Render Pix QR code
    User->>BCB: Pays via Pix
    BCB-->>API: Payment confirmed
    API->>API: KYT wallet risk check
    API->>Wallet: Route stablecoin
    API->>App: POST webhook: transaction.completed
    App->>User: Show confirmation
```

<Note>
  In Shared mode, Mippo generates a Sumsub `applicantId` for you when you call `kyc.submit`. You only need to pass `biometricHash` — the SDK handles the Sumsub embedding automatically.
</Note>

***

## Transaction states

Regardless of model, every transaction passes through the same state machine after KYC clears:

```mermaid theme={null}
stateDiagram-v2
    [*] --> CREATED: transactions.initiate()
    CREATED --> AWAITING_MFA: Auth challenge issued
    AWAITING_MFA --> AWAITING_PIX: MFA / liveness cleared
    AWAITING_PIX --> PROCESSING: Pix payment detected
    PROCESSING --> COMPLETED: Stablecoin settled on-chain
    PROCESSING --> FAILED: Settlement error (auto-retry x3)
    AWAITING_MFA --> EXPIRED: No MFA response within 15 min
    AWAITING_PIX --> EXPIRED: No Pix payment within 30 min
    FAILED --> [*]
    EXPIRED --> [*]
    COMPLETED --> [*]
```

Poll `mippo.transactions.status(transactionId)` or listen for webhooks — see the [Webhooks guide](/docs/guides/webhooks) for the event reference.
