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

# UnblockPay KYC (hosted link or documents)

> Provision an UnblockPay nest on an approved Avenia profile, then finish KYC via hosted Sumsub or by uploading documents through Deframe

UnblockPay KYC lives on the **same** `kycUserId` as Avenia. Top-level `status`, `brlaEnabled`, and `usdEnabled` stay Avenia. Read readiness from `ramps.unblockpay`.

Your customer must already have `extraRampProviders: ["unblockpay"]`. Omitted `rampProvider` is always Avenia.

There are **two client paths** after PII submit. Pick one per user; both share the same UnblockPay customer.

| Path | Who captures images | How review starts |
| - | - | - |
| **A — hosted Sumsub** | UnblockPay / Sumsub | User opens `ramps.unblockpay.verificationLink` |
| **B — own capture** | Your app | `POST /api/v1/kyc/sessions/:kycUserId/documents` then poll `GET /api/v1/kyc/status` |

<Info>
  There is no `POST .../check` route. `GET /api/v1/kyc/status` is the verification trigger. Path B calls UnblockPay `POST /v1/customers/{id}/check` once when uploads are recorded and `verification.pending` is empty. Path A never calls `/check`.
</Info>

## Prerequisites

1. Avenia KYC on this `kycUserId` is already `approved`.
2. The API key's customer is opted into UnblockPay.
3. You will send `rampProvider: "unblockpay"` on UnblockPay calls.

## 1. Submit PII (both paths)

`POST /api/v1/kyc/sessions/:kycUserId/submit`

UnblockPay submit is **PII only**. `documents` / `liveness` on this body return `422 VALIDATION_ERROR`.

```json theme={null}
{
  "rampProvider": "unblockpay",
  "firstName": "Maria",
  "lastName": "Silva",
  "phone": "+5511999999999",
  "dateOfBirth": "1990-05-15",
  "taxId": "52998224725",
  "taxIdCountry": "BRA",
  "email": "maria@example.com",
  "address": {
    "country": "BRA",
    "state": "SP",
    "city": "Sao Paulo",
    "zipCode": "01310100",
    "streetAddress": "Av Paulista",
    "number": "1"
  }
}
```

Identity is always `taxId` + `taxIdCountry`, and `taxIdCountry` is **required** on every UnblockPay submit. Send the CPF digits as `taxId` with `BRA` for Pix, or the local taxpayer id with its own ISO-3 country for foreign USD (for example `USA`). The country is part of the stored identity, so the same digits under two jurisdictions are two different identities. Public Pix and public USD are separate identities.

<Note>
  The legacy `cpf` field is still accepted as an alias for `taxId` with `taxIdCountry: "BRA"`, so existing integrations keep working. New integrations send `taxId`; do not send both.
</Note>

Response: top-level `status` is still Avenia. Read:

* `ramps.unblockpay.status`
* `ramps.unblockpay.verificationLink` (Path A)
* `ramps.unblockpay.verification.pending` (Path B checklist)

Submit is idempotent while the nest is pending: it returns the saved link and does not create a second UnblockPay customer.

## 2A. Hosted Sumsub

1. Open `ramps.unblockpay.verificationLink` for the user.
2. Poll `GET /api/v1/kyc/status?kycUserId=…`.
3. Ready when `ramps.unblockpay.status === "approved"` and the needed capability is `true` (`BRL` for Pix, `USD` for wire onramp).

Do **not** call `/documents` on this path. Status live-refreshes UnblockPay but does not start `/check`.

## 2B. Upload documents

Send **one file per request**. JPEG, PNG, or PDF, `base64` xor `url` (same transport as [external evidence](/guides/kyc/external-evidence)), up to 8MB. Files are streamed to UnblockPay and never stored. PDF is useful for a `PROOF_OF_ADDRESS` that arrives as a utility bill or bank statement; identity captures are photos.

`POST /api/v1/kyc/sessions/:kycUserId/documents`

```json theme={null}
{
  "rampProvider": "unblockpay",
  "documentType": "NATIONAL_ID",
  "documentSide": "FRONT",
  "country": "BRA",
  "file": { "base64": "/9j/4AAQ...", "mimeType": "image/jpeg" }
}
```

| Capture | `documentType` | `documentSide` |
| - | - | - |
| RG | `NATIONAL_ID` | `FRONT` and `BACK` |
| CNH | `DRIVER_LICENSE` | `FRONT` and `BACK` |
| Passport | `PASSPORT` | omit (single document; a side is accepted and passed through) |
| Utility bill ≤ 3 months | `PROOF_OF_ADDRESS` | omit |

v1 does **not** require a selfie. Upload identity FRONT + BACK and POA, then poll status. `GET /kyc/status` may still show `SELFIE` in `verification.pending`; that step does not block `/check` in this version.

Drive extra types from `ramps.unblockpay.verification.pending` only if you later opt into them — do not hardcode a selfie capture for v1.

Allowed nest statuses: `pending`, `partially_rejected`.

| Nest status | Upload result |
| - | - |
| `pending` / `partially_rejected` | Streams the file |
| `under_review` | `409 UNBLOCKPAY_KYC_IN_PROGRESS` |
| `rejected` | `409 UNBLOCKPAY_KYC_REJECTED` |
| `approved` | `200` no-op |
| missing nest | `409 UNBLOCKPAY_KYC_NOT_PROVISIONED` |

Omitted or `avenia` `rampProvider` → `400 RAMP_PROVIDER_NOT_ELIGIBLE`.

If `verification.partiallyRejected` has items, re-upload only those steps, then poll status again (`/check` may fire once more).

## 3. Poll status

`GET /api/v1/kyc/status?kycUserId=…`

When the UnblockPay nest exists and is not `approved` / `rejected`, Deframe live-refreshes UnblockPay (`GET /customers/{id}` + `GET /verification-details`). After Path B uploads, the first ready poll also calls UnblockPay `/check` once.

```json theme={null}
{
  "status": "approved",
  "brlaEnabled": true,
  "usdEnabled": false,
  "ramps": {
    "unblockpay": {
      "status": "under_review",
      "capabilities": { "BRL": false, "USD": false, "EUR": false },
      "verificationLink": "https://in.sumsub.com/websdk/p/EXAMPLE",
      "verification": {
        "type": "LIGHT",
        "pending": [],
        "underReview": ["NATIONAL_ID"],
        "approved": [],
        "partiallyRejected": [],
        "rejected": []
      }
    }
  }
}
```

`verificationLink` is `null` once nest status is no longer `pending` or `partially_rejected`.

USD offramp still needs `POST /api/v1/kyc/currency-unlock` with `rampProvider=unblockpay` and the bank fields in `ramps.unblockpay.unlock.requiredBodyFields`. See [Foreign USD KYC and ramps](/guides/kyc/usd-foreign-ramp).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.