> For the complete documentation index, see [llms.txt](https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/you-api/api-reference/build-with-llm.md).

# Build with LLM

Paste in Claude Code, Codex, or Cursor · ships the full integration.

## Data collection (Onboarding) flow

{% code title="prompt-you-onboarding.md" overflow="wrap" expandable="true" %}

````
# Integrate Unico You — Identity Data Collection

You are integrating **Unico You** identity onboarding into my application. The user completes a biometric-authenticated hosted flow and Unico returns verified personal data (name, government ID, phone, email, address, date of birth, etc.). Use this prompt when the goal is to collect and verify user identity — KYC, signup onboarding, account verification.

## My application context

<my_stack>
Stack: [framework + language — e.g. Next.js 15 + TypeScript, Django, Go, Rails]
Surface: [web | iOS | Android | React Native | Ionic | backend-only]
Scopes needed: [SCOPE_NAME | SCOPE_GOVERNMENT_ID | SCOPE_EMAIL | SCOPE_PHONE | SCOPE_ADDRESS | SCOPE_BIRTH_DATE | SCOPE_MOTHER_NAME | SCOPE_SEX | SCOPE_SELFIE]
Database: [Postgres | MySQL | Mongo | other]
</my_stack>

## Base URLs

| Environment | Base URL |
|---|---|
| UAT (testing) | `https://api.you.uat.unico.app` |
| Production | `https://api.you.unico.app` |

All endpoints use Bearer JWT authentication:
```
Authorization: Bearer <access_token>
```

## Step 1 — Obtain an access token (backend)

Authentication uses **OAuth2 Two-Legged (2LO)** with a service account and RS256-signed JWTs. All token exchange happens server-side — never expose credentials to the browser.

### 1.1 — Request a service account

Send the following to your Unico project manager:
- Name for the service account (up to 12 characters)
- Name, email, and cell phone (BR, US, or MX) of the responsible person

You will receive an email to generate your private key (`.pem`). UAT and Production require separate accounts.

After setup you will have: service account name, Tenant ID, private key file (`.key.pem`), and base JWT payload.

### 1.2 — Build and sign the JWT

```bash
# Payload
{
  "iss": "mycompany@abc123.iam.acesso.io",    # service_account@tenant_id.iam.acesso.io
  "aud": "https://identityhomolog.acesso.io", # UAT — use https://identity.acesso.io for prod
  "scope": "*",
  "iat": <unix_timestamp_now>,
  "exp": <iat + 3600>                         # max 1 hour; cannot be a fixed value
}
# Header: {"alg":"RS256","typ":"JWT"} — sign with RS256 using your .key.pem
```

### 1.3 — Exchange JWT for access token

```bash
# UAT
curl -X POST https://identityhomolog.acesso.io/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
  --data-urlencode "assertion=<signed_jwt>"

# Production: POST https://identity.acesso.io/oauth2/token  (same body)
```

Response:
```json
{ "access_token": "<token>", "token_type": "Bearer", "expires_in": "3600" }
```

Store as `YOU_ACCESS_TOKEN`. Renew when ≤ 10 minutes remain before expiration.

## Step 2 — Choose the scopes to collect

| Scope | Data returned |
|---|---|
| `SCOPE_NAME` | Full name |
| `SCOPE_GOVERNMENT_ID` | CPF or other government-issued ID |
| `SCOPE_PHONE` | Phone number |
| `SCOPE_EMAIL` | Email address |
| `SCOPE_ADDRESS` | Full residential address |
| `SCOPE_BIRTH_DATE` | Date of birth (YYYY-MM-DD) |
| `SCOPE_MOTHER_NAME` | Mother's full name |
| `SCOPE_SEX` | Sex: M, F, or I |
| `SCOPE_SELFIE` | Biometric selfie capture |

Combine any subset freely. `SCOPE_PAYMENT` and `SCOPE_MAJORITY` are different flows — see their dedicated prompts.

## Step 3 — Create a process (backend)

```bash
curl -X POST https://api.you.uat.unico.app/api/public/v1/process \
  -H "Authorization: Bearer $YOU_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scopes": [
      "SCOPE_NAME",
      "SCOPE_GOVERNMENT_ID",
      "SCOPE_EMAIL",
      "SCOPE_PHONE",
      "SCOPE_ADDRESS"
    ],
    "branchId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "redirectUrl": "https://yourapp.com/verification/callback"
  }'
```

Response:
```json
{
  "id": "a1b2c3d4-0000-0000-0000-abcdef012345",
  "token": "eyJhbGciOiJSUzI1NiJ9...",
  "link": "https://you.unico.io/..."
}
```

**Persist `id`** — needed to retrieve results. `token` initializes the Web SDK. `link` is the URL for native app redirects.

Optional fields:

| Field | Notes |
|---|---|
| `redirectUrl` | URL the user is sent to after the flow finishes |
| `appCallback` | Deep link for native apps (not needed for this flow unless you also use PIX) |

## Step 4 — Install the SDK that matches my stack

| Stack | Package | Install |
|---|---|---|
| Web (React, Vue, Next.js, Nuxt, Svelte, vanilla JS) | `idpay-b2b-sdk` (min v2.2.3) | `npm install idpay-b2b-sdk` |
| iOS (Swift / SwiftUI) | `AuthenticationServices` (native) | No install — system framework (iOS 13+) |
| Android (Kotlin) | `androidx.browser` | `implementation("androidx.browser:browser:1.5.0")` |
| React Native | Native module (custom bridge) | No npm package — implement native module (see Step 5) |
| Ionic | Capacitor plugin (local) | `npm install @capacitor/app` + custom plugin (see Step 5) |
| Backend-only | none | Call REST directly |

> **Important for web:** Register your HTTPS domain with the Unico team before going live. The SDK uses an iframe and will only render on pre-approved domains. Do **not** embed it via a plain `<iframe>` HTML tag or inside a WebView.
## Step 5 — Present the flow to the user

### Web

```javascript
import { YouSDK } from "idpay-b2b-sdk";

// 1. Initialize BEFORE creating the process (pre-loads assets, detects device compatibility)
//    Call this on page load — process creation must only happen after this resolves.
const initResult = await YouSDK.init({
  env: "uat" // omit in production
});

if (initResult?.statusPreValidation !== "DEVICE_SUPPORTED") {
  // Handle incompatible device — do not proceed to process creation
  return;
}

// 2. Create the process on your backend (POST /api/public/v1/process)
//    then open the You flow with the returned token and id
YouSDK.open({
  transactionId: processId,   // `id` from create-process response
  token: processToken,        // `token` from create-process response
  onFinish: (process) => {
    // process = { captureConcluded, concluded, id }
    // Fetch results from your backend using process.id
  }
});

// Close explicitly if needed
YouSDK.close();
```
### Android (Custom Tabs)
```kotlin
// app/build.gradle: implementation("androidx.browser:browser:1.5.0")

class CustomTabActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val url = intent.getStringExtra("PROCESS_URL") ?: return
        CustomTabsIntent.Builder().build().launchUrl(this, Uri.parse(url))
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        val url = intent.data ?: return
        if (url.scheme == "YOUR_SCHEME") {
            // End of process (redirectUrl) — handle result here
        }
    }
}

// AndroidManifest.xml — inside <activity>, launchMode="singleTop"
// <intent-filter>
//   <action android:name="android.intent.action.VIEW" />
//   <category android:name="android.intent.category.DEFAULT" />
//   <category android:name="android.intent.category.BROWSABLE" />
//   <data android:scheme="YOUR_SCHEME" android:host="callback"/>
// </intent-filter>

// Launch:
startActivity(Intent(context, CustomTabActivity::class.java).apply {
    putExtra("PROCESS_URL", processLink)
})
```
Pass `redirectUrl` when creating the process:
```json
{ "redirectUrl": "YOUR_SCHEME://callback" }
```
### iOS (ASWebAuthenticationSession)
```swift
import AuthenticationServices

class UnicoAuthenticationController: NSObject, ASWebAuthenticationPresentationContextProviding {
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
            .first ?? ASPresentationAnchor()
    }
}

let unicoController = UnicoAuthenticationController()

func startYouFlow(url: URL) {
    let session = ASWebAuthenticationSession(
        url: url,
        callbackURLScheme: "YOUR_SCHEME"  // prefix of redirectUrl
    ) { callbackURL, error in
        // callbackURL = YOUR_SCHEME://callback — handle result
    }
    session.presentationContextProvider = unicoController
    session.prefersEphemeralWebBrowserSession = true
    session.start()
}

// Start:
startYouFlow(url: URL(string: processLink)!)
```
Register `YOUR_SCHEME` under `CFBundleURLTypes` in `Info.plist`.
### React Native
```typescript
// src/modules/UnicoModule.ts
import { NativeModules } from "react-native";
export const Unico = {
  openSession: (url: string): Promise<string> => NativeModules.UnicoModule.openSession(url),
};

// Start the flow
const resultUrl = await Unico.openSession(processLink);
```
Native implementations required:
- **iOS**: `UnicoModule.m` (ObjC bridge) + `UnicoModule.swift` wrapping `ASWebAuthenticationSession`
- **Android**: `UnicoModule.kt` with Custom Tabs + `UnicoPackage.kt` + registration in `MainApplication.kt` + `onNewIntent` in `MainActivity.kt`
- **Both**: configure `AndroidManifest.xml` intent-filter and `Info.plist` URL scheme for `YOUR_SCHEME`
### Ionic (Capacitor)
```typescript
// src/plugins/unico-plugin.ts
import { registerPlugin } from "@capacitor/core";
export interface UnicoPlugin {
  openSession(options: { url: string }): Promise<{ url: string }>;
}
export const Unico = registerPlugin<UnicoPlugin>("Unico");

// Start the flow
const result = await Unico.openSession({ url: processLink });
```
Native implementations required:
- **iOS**: `UnicoPlugin.swift` wrapping `ASWebAuthenticationSession`, registered via Capacitor
- **Android**: `UnicoPlugin.kt` with Custom Tabs + registration in `MainActivity.kt`
- **Both**: configure URL scheme in `capacitor.config`, `AndroidManifest.xml`, and `Info.plist`
## Step 6 — Retrieve results (backend)
```bash
curl https://api.you.uat.unico.app/api/public/v1/process/{id} \
  -H "Authorization: Bearer $YOU_ACCESS_TOKEN"
```
Response:
```json
{
  "id": "a1b2c3d4-0000-0000-0000-abcdef012345",
  "status": "completed",
  "data": {
    "name":         { "value": "João da Silva",    "status": "validated" },
    "governmentId": { "value": "000.000.000-00",   "status": "validated" },
    "email":        { "value": "joao@example.com", "status": "not_validated" },
    "phone":        { "value": "11912345678",       "status": "validated" },
    "birthDate":    { "value": "1990-05-15",        "status": "validated" },
    "address": {
      "street": "Rua das Flores", "number": "42", "complement": "Apto 10",
      "neighborhood": "Centro", "city": "São Paulo", "state": "SP",
      "zipCode": "01310-100", "status": "validated"
    }
  }
}
```
### Process statuses
| Status | Meaning |
|---|---|
| `pending` | Process created, user has not started |
| `processing` | User completed the flow, results being processed |
| `completed` | Data is ready to retrieve |
| `expired` | Link expired before user completed the flow |
| `canceled` | Process was canceled |
### Data field statuses
| Status | Meaning |
|---|---|
| `validated` | Data verified with high confidence by Unico |
| `not_validated` | Data was collected but could not be verified |
| `empty` | No data available for this field |
## Step 7 — Apply the result to my database
```typescript
switch (process.status) {
  case "completed":
    user.onboardingStatus = "completed";
    if (process.data.name?.status === "validated")
      user.name = process.data.name.value;
    if (process.data.governmentId?.status === "validated")
      user.cpf = process.data.governmentId.value;
    // store other fields as needed, respecting their status
    break;
  case "expired":
    user.onboardingStatus = "expired";
    // re-create a process if needed
    break;
  case "canceled":
    user.onboardingStatus = "canceled";
    break;
  case "processing":
    user.onboardingStatus = "processing";
    // poll again shortly
    break;
  case "pending":
    // user hasn't started yet
    break;
}
```
## Error reference
| HTTP | Code | Message | Cause |
|---|---|---|---|
| 400 | 3 | `scopes is required` | `scopes` field missing |
| 400 | 3 | `scope "%s" invalid` | Unknown scope value |
| 400 | 3 | `branch id "%s" invalid` | Invalid `branchId` UUID |
| 401 | — | `Jwt is expired` | Token expired — re-authenticate |
| 401 | — | `failed to authorize request: no branch in request` | Invalid `branchId` |
| 429 | — | _(no body)_ | Rate limit — implement exponential back-off |
| 500 | 13 | `internal error` | — |
## Best-practice checklist before you mark this done
- [ ] Service account private key (`.pem`) and `YOU_BRANCH_ID` stored in secrets manager. Never committed.
- [ ] Backend token-exchange logic: RS256-signed JWT → `POST /oauth2/token` → cache for `expires_in - 600` seconds, then renew.
- [ ] Backend route that creates the process, persists `id`, and returns `token` + `link` to the frontend.
- [ ] Web: `YouSDK.init()` called on page load, **before** process creation. HTTPS domain registered with Unico team.
- [ ] Native: `redirectUrl` URL scheme registered in `AndroidManifest.xml` intent-filter and `Info.plist`.
- [ ] Backend result-fetch endpoint calls `GET /api/public/v1/process/{id}` and maps `status` + `data` to the user model.
- [ ] Data field `status` values (`validated`, `not_validated`, `empty`) handled according to business requirements.
- [ ] All five process statuses handled case-sensitively.
- [ ] User-facing consent shown BEFORE the process link is opened.
- [ ] Access token never sent to the browser.
## When you need more detail
- Authentication: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/authentication/obtaining-credentials`
- Getting started: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/you-api/api-reference/getting-started`
- Identity data collection flow: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/you-api/api-reference/data-collection-onboarding-flow`
- Web SDK: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/web`
- Android: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/android/customtabs`
- iOS: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/ios/aswebauthenticationsession`
- Ionic: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/ionic`
- React Native: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/react-native`

Now build the integration. If anything in `## My application context` is missing, ask once at the top of your reply, then ship the complete change set: token-exchange logic, backend route to create the process, frontend SDK call or native link handling, result-fetch endpoint, and DB update logic. Keep the access token server-side only and handle all five process statuses case-sensitively.
````

{% endcode %}

## Payment flow

{% code title="prompt-you-payment.md" overflow="wrap" expandable="true" %}

````
# Integrate Unico You — Payment Flow

You are integrating **Unico You** payment flow into my application. The user confirms their identity biometrically and authorizes a payment (PIX or card) in a single hosted session — no separate KYC step required. Use this prompt when the goal is to process a payment with built-in identity verification.

> **Exclusive scope:** `SCOPE_PAYMENT` cannot be combined with any other scope.
## My application context

<my_stack>
Stack: [framework + language — e.g. Next.js 15 + TypeScript, Django, Go, Rails]
Surface: [web | iOS | Android | React Native | Ionic | backend-only]
Payment method: [PIX | card | both]
Database: [Postgres | MySQL | Mongo | other]
</my_stack>

## Base URLs

| Environment | Base URL |
|---|---|
| UAT (testing) | `https://api.you.uat.unico.app` |
| Production | `https://api.you.unico.app` |

All endpoints use Bearer JWT authentication:
```
Authorization: Bearer <access_token>
```

## Step 1 — Obtain an access token (backend)

Authentication uses **OAuth2 Two-Legged (2LO)** with a service account and RS256-signed JWTs. All token exchange happens server-side.

### 1.1 — Request a service account

Send to your Unico project manager: service account name (up to 12 chars), name, email, and cell phone (BR, US, or MX) of the responsible person. UAT and Production require separate accounts. You will receive a private key (`.pem`) by email.

### 1.2 — Build and sign the JWT

```bash
# Payload
{
  "iss": "mycompany@abc123.iam.acesso.io",    # service_account@tenant_id.iam.acesso.io
  "aud": "https://identityhomolog.acesso.io", # UAT — use https://identity.acesso.io for prod
  "scope": "*",
  "iat": <unix_timestamp_now>,
  "exp": <iat + 3600>                         # max 1 hour; cannot be a fixed value
}
# Header: {"alg":"RS256","typ":"JWT"} — sign with RS256 using your .key.pem
```

### 1.3 — Exchange JWT for access token

```bash
curl -X POST https://identityhomolog.acesso.io/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
  --data-urlencode "assertion=<signed_jwt>"
# Production: POST https://identity.acesso.io/oauth2/token
```

Response:
```json
{ "access_token": "<token>", "token_type": "Bearer", "expires_in": "3600" }
```

Store as `YOU_ACCESS_TOKEN`. Renew when ≤ 10 minutes remain before expiration.

## Step 2 — Create a process (backend)

`SCOPE_PAYMENT` requires the payer's identity and payment details upfront. The response `id`, `token`, and `link` are the same regardless of payment method.

### PIX payment

```bash
curl -X POST https://api.you.uat.unico.app/api/public/v1/process \
  -H "Authorization: Bearer $YOU_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scopes": ["SCOPE_PAYMENT"],
    "branchId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "redirectUrl": "https://yourapp.com/payment/callback",
    "person": {
      "name": "João da Silva",
      "identity": {
        "key": "IDENTITY_KEY_CPF",
        "value": "00000000000"
      }
    },
    "scopeConfig": {
      "payment": {
        "amount": {
          "currency": "BRL",
          "value": 15000
        },
        "pixConfiguration": {
          "accountKey": "chave-pix-destinatario",
          "priorityParticipants": ["nubank", "itau", "bradesco"]
        }
      }
    }
  }'
```

PIX options:
- `accountKey` — recipient PIX key
- `qrCode` — EMV QR code string (`accountKey` and `qrCode` are mutually exclusive)
- `priorityParticipants` — optional list of bank slugs to prioritize

### Card payment

```bash
curl -X POST https://api.you.uat.unico.app/api/public/v1/process \
  -H "Authorization: Bearer $YOU_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scopes": ["SCOPE_PAYMENT"],
    "branchId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "redirectUrl": "https://yourapp.com/payment/callback",
    "person": {
      "name": "João da Silva",
      "identity": { "key": "IDENTITY_KEY_CPF", "value": "00000000000" }
    },
    "scopeConfig": {
      "payment": {
        "amount": { "currency": "BRL", "value": 15000 },
        "cardConfiguration": {
          "currency": "BRL",
          "installments": [
            { "quotes": 1, "amount": 15000, "totalAmount": 15000, "hasInterest": false },
            { "quotes": 3, "amount": 5100,  "totalAmount": 15300, "hasInterest": true }
          ]
        }
      }
    }
  }'
```

Response (both payment types):
```json
{
  "id": "a1b2c3d4-0000-0000-0000-abcdef012345",
  "token": "eyJhbGciOiJSUzI1NiJ9...",
  "link": "https://you.unico.io/..."
}
```

**Persist `id`** — needed to retrieve results and generate cryptograms. `token` initializes the Web SDK. `link` is the URL for native app redirects.

**For native apps on PIX**, also pass `appCallback` (the deep link the bank uses to return the user after consent):
```json
{
  "redirectUrl": "YOUR_SCHEME://callback",
  "appCallback": "YOUR_RETURN_SCHEME://callback"
}
```

## Step 3 — Install the SDK that matches my stack

| Stack | Package | Install |
|---|---|---|
| Web (React, Vue, Next.js, Nuxt, Svelte, vanilla JS) | `idpay-b2b-sdk` (min v2.2.3) | `npm install idpay-b2b-sdk` |
| iOS (Swift / SwiftUI) | `AuthenticationServices` (native) | No install — system framework (iOS 13+) |
| Android (Kotlin) | `androidx.browser` | `implementation("androidx.browser:browser:1.5.0")` |
| React Native | Native module (custom bridge) | No npm package — implement native module (see Step 4) |
| Ionic | Capacitor plugin (local) | `npm install @capacitor/app` + custom plugin (see Step 4) |
| Backend-only | none | Call REST directly |

> **Important for web:** Register your HTTPS domain with the Unico team before going live. Do **not** embed the SDK inside a WebView or via a plain `<iframe>` tag.
## Step 4 — Present the flow to the user

### Web

```javascript
import { YouSDK } from "idpay-b2b-sdk";

// 1. Initialize BEFORE creating the process (pre-loads assets, detects device compatibility)
//    Call this on page load — process creation must only happen after this resolves.
const initResult = await YouSDK.init({
  env: "uat",       // omit in production
  hasPayments: true // required for payment flow
});

if (initResult?.statusPreValidation !== "DEVICE_SUPPORTED") {
  // Handle incompatible device — do not proceed to process creation
  return;
}

// 2. Create the process on your backend (POST /api/public/v1/process)
//    then open the You flow with the returned token and id
YouSDK.open({
  transactionId: processId,
  token: processToken,
  onFinish: (process) => {
    // process = { captureConcluded, concluded, id }
    // Fetch results from your backend using process.id
  }
});
```
> **Web + PIX:** no `appCallback` needed — the bank consent redirect stays within the same browser session.
### Android (Custom Tabs)
The PIX flow redirects the user to the bank app. You need two URL schemes and must handle the bank's return in `onNewIntent`.
```kotlin
// app/build.gradle: implementation("androidx.browser:browser:1.5.0")

class CustomTabActivity : ComponentActivity() {
    private var lastUrl: String? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val url = intent.getStringExtra("PROCESS_URL") ?: return
        openCustomTab(url)
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        val url = intent.data ?: return
        when (url.scheme) {
            "YOUR_RETURN_SCHEME" -> lastUrl?.let { openCustomTab(it) } // bank return (PIX)
            "YOUR_SCHEME"        -> { /* end of process — handle result */ }
        }
    }

    fun openCustomTab(url: String) {
        lastUrl = url
        CustomTabsIntent.Builder().build().launchUrl(this, Uri.parse(url))
    }
}

// AndroidManifest.xml — inside <activity>, launchMode="singleTop"
// <uses-permission android:name="android.permission.CAMERA"/>
// Two intent-filters:
//   <data android:scheme="YOUR_SCHEME"        android:host="callback"/> ← redirectUrl
//   <data android:scheme="YOUR_RETURN_SCHEME" android:host="callback"/> ← appCallback

// Launch:
startActivity(Intent(context, CustomTabActivity::class.java).apply {
    putExtra("PROCESS_URL", processLink)
})
```
### iOS (ASWebAuthenticationSession)
```swift
import AuthenticationServices

class UnicoAuthenticationController: NSObject, ASWebAuthenticationPresentationContextProviding {
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
            .first ?? ASPresentationAnchor()
    }
}

let unicoController = UnicoAuthenticationController()

class AuthenticationSessionManager {
    static let shared = AuthenticationSessionManager()
    private var session: ASWebAuthenticationSession?
    private var lastURL: URL?

    func start(url: URL) {
        lastURL = url
        let s = ASWebAuthenticationSession(url: url, callbackURLScheme: "YOUR_SCHEME") { _, _ in
            self.session = nil
        }
        s.presentationContextProvider = unicoController
        s.prefersEphemeralWebBrowserSession = true
        s.start()
        session = s
    }

    // Called after the bank returns via appCallback
    func cancelAndRestart() {
        session?.cancel(); session = nil
        guard let url = lastURL else { return }
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { self.start(url: url) }
    }
}

// App entry point — intercept bank return (PIX appCallback)
@main struct YourApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onOpenURL { url in
                    guard url.scheme == "YOUR_RETURN_SCHEME" else { return }
                    AuthenticationSessionManager.shared.cancelAndRestart()
                }
        }
    }
}

// Info.plist — register YOUR_RETURN_SCHEME under CFBundleURLTypes
// Start the flow:
AuthenticationSessionManager.shared.start(url: URL(string: processLink)!)
```
### React Native
```typescript
// src/modules/UnicoModule.ts
import { NativeModules, Linking } from "react-native";
export const Unico = {
  openSession: (url: string): Promise<string> => NativeModules.UnicoModule.openSession(url),
  cancelAndRestart: (): Promise<void> => NativeModules.UnicoModule.cancelAndRestart(),
};

// App.tsx — intercept PIX bank return
useEffect(() => {
  const sub = Linking.addEventListener("url", ({ url }) => {
    if (url.startsWith("YOUR_RETURN_SCHEME://")) Unico.cancelAndRestart();
  });
  return () => sub.remove();
}, []);

// Start the flow
const resultUrl = await Unico.openSession(processLink);
```
Native implementations required:
- **iOS**: `UnicoModule.m` (ObjC bridge) + `UnicoModule.swift` wrapping `ASWebAuthenticationSession` with `cancelAndRestart()`
- **Android**: `UnicoModule.kt` with Custom Tabs + `UnicoPackage.kt` + `MainApplication.kt` registration + `onNewIntent` in `MainActivity.kt`
- **Both**: configure `AndroidManifest.xml` (two intent-filters) and `Info.plist` (`YOUR_RETURN_SCHEME`)
### Ionic (Capacitor)
```typescript
// src/plugins/unico-plugin.ts
import { registerPlugin } from "@capacitor/core";
export interface UnicoPlugin {
  openSession(options: { url: string }): Promise<{ url: string }>;
  cancelAndRestart(): Promise<void>;
}
export const Unico = registerPlugin<UnicoPlugin>("Unico");

// app.component.ts — intercept PIX bank return
App.addListener("appUrlOpen", ({ url }) => {
  if (url.startsWith("YOUR_RETURN_SCHEME://")) Unico.cancelAndRestart();
});

// Start the flow
const result = await Unico.openSession({ url: processLink });
```
Native implementations required:
- **iOS**: `UnicoPlugin.swift` with `ASWebAuthenticationSession` and `cancelAndRestart()`
- **Android**: `UnicoPlugin.kt` with Custom Tabs, registered in `MainActivity.kt`
- **Both**: configure URL schemes in `capacitor.config`, `AndroidManifest.xml`, and `Info.plist`
## Step 5 — Retrieve results (backend)
```bash
curl https://api.you.uat.unico.app/api/public/v1/process/{id} \
  -H "Authorization: Bearer $YOU_ACCESS_TOKEN"
```
Response:
```json
{
  "id": "a1b2c3d4-0000-0000-0000-abcdef012345",
  "status": "completed",
  "data": {
    "payment": {
      "proposal": {
        "payments": [
          {
            "method": "pix",
            "status": "completed",
            "amount": { "currency": "BRL", "value": "150.00" },
            "instrument": {
              "payer":    { "name": "João da Silva", "identifier": "000.000.000-00", "bankName": "Nubank" },
              "receiver": { "name": "Empresa Exemplo", "bankName": "Itaú" }
            }
          }
        ]
      }
    }
  }
}
```
### Process statuses
| Status | Meaning |
|---|---|
| `pending` | Process created, user has not started |
| `processing` | User completed the flow, results being processed |
| `completed` | Payment data is ready |
| `expired` | Link expired before user completed the flow |
| `canceled` | Process was canceled |
### Payment entry statuses
| Status | Meaning |
|---|---|
| `pending` | Payment initiated |
| `processing` | Payment in progress |
| `completed` | Payment successful |
| `failed` | Payment failed |
| `canceled` | Payment canceled |
## Step 6 — Generate cryptogram (card payments only)
After the process is `completed` for a card payment, generate the cryptogram for your payment gateway.
```bash
curl -X POST https://api.you.uat.unico.app/api/public/v1/process/cryptogram \
  -H "Authorization: Bearer $YOU_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{ "id": "a1b2c3d4-0000-0000-0000-abcdef012345" }'
```
Response:
```json
{
  "id": "65eb98f1-1c58-42dd-a428-839fc7818a7b",
  "intents": [
    {
      "intentId": 42,
      "token": "hONCBWfwCuqsazeVsV",
      "cryptogram": "ITepZsir3mMNhAlV",
      "eci": "05",
      "expirationDate": "2026-12",
      "amount": { "value": 10000, "currency": "BRL" },
      "installments": {
        "quotes": 3,
        "amount": { "value": 3334, "currency": "BRL" }
      }
    }
  ]
}
```
Pass `token`, `cryptogram`, and `eci` to your payment gateway to complete the charge.
## Step 7 — Apply the result to my database
```typescript
switch (process.status) {
  case "completed":
    const payment = process.data.payment.proposal.payments[0];
    order.paymentStatus = payment.status;     // "completed", "failed", etc.
    order.paymentMethod = payment.method;     // "pix" or "credit_card"
    order.payer = payment.instrument.payer.name;
    // for card: call POST /process/cryptogram and submit to gateway
    break;
  case "expired":
    order.status = "expired";
    // re-create a process if needed
    break;
  case "canceled":
    order.status = "canceled";
    break;
  case "processing":
    order.status = "processing";
    // poll again shortly
    break;
  case "pending":
    // user hasn't started yet
    break;
}
```
## Error reference
| HTTP | Code | Message | Cause |
|---|---|---|---|
| 400 | 3 | `scopes is required` | `scopes` field missing |
| 400 | 3 | `scope "%s" invalid` | Unknown scope value |
| 400 | 3 | `branch id "%s" invalid` | Invalid `branchId` UUID |
| 401 | — | `Jwt is expired` | Token expired — re-authenticate |
| 401 | — | `failed to authorize request: no branch in request` | Invalid `branchId` |
| 429 | — | _(no body)_ | Rate limit — implement exponential back-off |
| 500 | 13 | `internal error` | — |
## Best-practice checklist before you mark this done
- [ ] Service account private key (`.pem`) and `YOU_BRANCH_ID` stored in secrets manager. Never committed.
- [ ] Backend token-exchange logic: RS256-signed JWT → `POST /oauth2/token` → cache for `expires_in - 600` seconds, then renew.
- [ ] Backend route that creates the process with `person` + `scopeConfig.payment`, persists `id`, and returns `token` + `link`.
- [ ] Web: `YouSDK.init({ hasPayments: true })` called on page load, **before** process creation. HTTPS domain registered with Unico team.
- [ ] Native apps (PIX): `redirectUrl` and `appCallback` URL schemes registered in `AndroidManifest.xml` (two intent-filters) and `Info.plist`.
- [ ] Native apps (PIX): bank return (`appCallback`) handled — Custom Tab reopened (Android) / session cancelled and restarted (iOS) after bank consent.
- [ ] Backend result-fetch endpoint calls `GET /api/public/v1/process/{id}` and maps payment status to the order model.
- [ ] All five process statuses handled case-sensitively.
- [ ] Card payments: `POST /api/public/v1/process/cryptogram` called after `completed`; `token`, `cryptogram`, and `eci` submitted to gateway.
- [ ] Access token never sent to the browser.
## When you need more detail
- Authentication: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/authentication/obtaining-credentials`
- Getting started: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/you-api/api-reference/getting-started`
- Payment flow: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/you-api/api-reference/payment-flow`
- Web SDK: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/web`
- Android: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/android/customtabs`
- iOS: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/ios/aswebauthenticationsession`
- Ionic: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/ionic`
- React Native: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/react-native`

Now build the integration. If anything in `## My application context` is missing, ask once at the top of your reply, then ship the complete change set: token-exchange logic, backend route to create the process (with `person` and `scopeConfig.payment`), frontend SDK call or native link handling, PIX `appCallback` handling on native apps, result-fetch endpoint, DB update logic, and (for card) cryptogram generation + gateway submission. Keep the access token server-side only and handle all five process statuses case-sensitively.
````

{% endcode %}

## Age verification flow

{% code title="prompt-you-age.md" overflow="wrap" expandable="true" %}

````
# Integrate Unico You — Age Verification Flow

You are integrating **Unico You** age-majority verification into my application. The user completes a biometric verification and Unico returns whether they are 18 or older — **no personal data is collected or stored**. No CPF, no name, no document required.

Use this prompt for: age gates on regulated content or services, compliance checks requiring proof of majority without storing PII, scenarios where the user's identity is already known but age needs to be separately confirmed.

> **Exclusive scope:** `SCOPE_MAJORITY` cannot be combined with any other scope.
## My application context

<my_stack>
Stack: [framework + language — e.g. Next.js 15 + TypeScript, Django, Go, Rails]
Surface: [web | iOS | Android | React Native | Ionic | backend-only]
Database: [Postgres | MySQL | Mongo | other]
</my_stack>

## Base URLs

| Environment | Base URL |
|---|---|
| UAT (testing) | `https://api.you.uat.unico.app` |
| Production | `https://api.you.unico.app` |

All endpoints use Bearer JWT authentication:
```
Authorization: Bearer <access_token>
```

## Step 1 — Obtain an access token (backend)

Authentication uses **OAuth2 Two-Legged (2LO)** with a service account and RS256-signed JWTs. All token exchange happens server-side.

### 1.1 — Request a service account

Send to your Unico project manager: service account name (up to 12 chars), name, email, and cell phone (BR, US, or MX) of the responsible person. UAT and Production require separate accounts. You will receive a private key (`.pem`) by email.

### 1.2 — Build and sign the JWT

```bash
# Payload
{
  "iss": "mycompany@abc123.iam.acesso.io",    # service_account@tenant_id.iam.acesso.io
  "aud": "https://identityhomolog.acesso.io", # UAT — use https://identity.acesso.io for prod
  "scope": "*",
  "iat": <unix_timestamp_now>,
  "exp": <iat + 3600>                         # max 1 hour; cannot be a fixed value
}
# Header: {"alg":"RS256","typ":"JWT"} — sign with RS256 using your .key.pem
```

### 1.3 — Exchange JWT for access token

```bash
curl -X POST https://identityhomolog.acesso.io/oauth2/token \
  -H "Content-Type: application/x-www-form-urlencoded" \
  --data-urlencode "grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer" \
  --data-urlencode "assertion=<signed_jwt>"
# Production: POST https://identity.acesso.io/oauth2/token
```

Response:
```json
{ "access_token": "<token>", "token_type": "Bearer", "expires_in": "3600" }
```

Store as `YOU_ACCESS_TOKEN`. Renew when ≤ 10 minutes remain before expiration.

## Step 2 — Create a process (backend)

No `person` or `scopeConfig` fields are needed — the flow is fully anonymous.

```bash
curl -X POST https://api.you.uat.unico.app/api/public/v1/process \
  -H "Authorization: Bearer $YOU_ACCESS_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "scopes": ["SCOPE_MAJORITY"],
    "branchId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
    "redirectUrl": "https://yourapp.com/age-check/callback"
  }'
```

Response:
```json
{
  "id": "a1b2c3d4-0000-0000-0000-abcdef012345",
  "token": "eyJhbGciOiJSUzI1NiJ9...",
  "link": "https://you.unico.io/..."
}
```

**Persist `id`** — needed to retrieve the result. `token` initializes the Web SDK. `link` is the URL for native app redirects.

## Step 3 — Install the SDK that matches my stack

| Stack | Package | Install |
|---|---|---|
| Web (React, Vue, Next.js, Nuxt, Svelte, vanilla JS) | `idpay-b2b-sdk` (min v2.2.3) | `npm install idpay-b2b-sdk` |
| iOS (Swift / SwiftUI) | `AuthenticationServices` (native) | No install — system framework (iOS 13+) |
| Android (Kotlin) | `androidx.browser` | `implementation("androidx.browser:browser:1.5.0")` |
| React Native | Native module (custom bridge) | No npm package — implement native module (see Step 4) |
| Ionic | Capacitor plugin (local) | `npm install @capacitor/app` + custom plugin (see Step 4) |
| Backend-only | none | Call REST directly |

> **Important for web:** Register your HTTPS domain with the Unico team before going live. Do **not** embed the SDK via a plain `<iframe>` tag or inside a WebView.
## Step 4 — Present the flow to the user

There is no bank redirect or `appCallback` in this flow — one URL scheme is sufficient on all native platforms.

### Web

```javascript
import { YouSDK } from "idpay-b2b-sdk";

// 1. Initialize BEFORE creating the process (pre-loads assets, detects device compatibility)
//    Call this on page load — process creation must only happen after this resolves.
const initResult = await YouSDK.init({
  env: "uat" // omit in production
});

if (initResult?.statusPreValidation !== "DEVICE_SUPPORTED") {
  // Handle incompatible device — do not proceed to process creation
  return;
}

// 2. Create the process on your backend (POST /api/public/v1/process)
//    then open the You flow with the returned token and id
YouSDK.open({
  transactionId: processId,
  token: processToken,
  onFinish: (process) => {
    // process = { captureConcluded, concluded, id }
    // Fetch the result from your backend using process.id
  }
});
```
### Android (Custom Tabs)
```kotlin
// app/build.gradle: implementation("androidx.browser:browser:1.5.0")

class CustomTabActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val url = intent.getStringExtra("PROCESS_URL") ?: return
        CustomTabsIntent.Builder().build().launchUrl(this, Uri.parse(url))
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        val url = intent.data ?: return
        if (url.scheme == "YOUR_SCHEME") {
            // End of process (redirectUrl) — handle result here
        }
    }
}

// AndroidManifest.xml — inside <activity>, launchMode="singleTop"
// <intent-filter>
//   <action android:name="android.intent.action.VIEW" />
//   <category android:name="android.intent.category.DEFAULT" />
//   <category android:name="android.intent.category.BROWSABLE" />
//   <data android:scheme="YOUR_SCHEME" android:host="callback"/>
// </intent-filter>

// Launch:
startActivity(Intent(context, CustomTabActivity::class.java).apply {
    putExtra("PROCESS_URL", processLink)
})
```
### iOS (ASWebAuthenticationSession)
```swift
import AuthenticationServices

class UnicoAuthenticationController: NSObject, ASWebAuthenticationPresentationContextProviding {
    func presentationAnchor(for session: ASWebAuthenticationSession) -> ASPresentationAnchor {
        UIApplication.shared.connectedScenes
            .compactMap { $0 as? UIWindowScene }
            .flatMap { $0.windows }
            .first ?? ASPresentationAnchor()
    }
}

let unicoController = UnicoAuthenticationController()

func startYouFlow(url: URL) {
    let session = ASWebAuthenticationSession(
        url: url,
        callbackURLScheme: "YOUR_SCHEME"  // prefix of redirectUrl
    ) { callbackURL, error in
        // callbackURL = YOUR_SCHEME://callback — handle result
    }
    session.presentationContextProvider = unicoController
    session.prefersEphemeralWebBrowserSession = true
    session.start()
}

startYouFlow(url: URL(string: processLink)!)
```
Register `YOUR_SCHEME` under `CFBundleURLTypes` in `Info.plist`.
### React Native
```typescript
// src/modules/UnicoModule.ts
import { NativeModules } from "react-native";
export const Unico = {
  openSession: (url: string): Promise<string> => NativeModules.UnicoModule.openSession(url),
};

const resultUrl = await Unico.openSession(processLink);
```
Native implementations required:
- **iOS**: `UnicoModule.m` (ObjC bridge) + `UnicoModule.swift` wrapping `ASWebAuthenticationSession`
- **Android**: `UnicoModule.kt` with Custom Tabs + `UnicoPackage.kt` + `MainApplication.kt` + `onNewIntent` in `MainActivity.kt`
- **Both**: configure `AndroidManifest.xml` intent-filter and `Info.plist` URL scheme for `YOUR_SCHEME`
### Ionic (Capacitor)
```typescript
import { registerPlugin } from "@capacitor/core";
export interface UnicoPlugin {
  openSession(options: { url: string }): Promise<{ url: string }>;
}
export const Unico = registerPlugin<UnicoPlugin>("Unico");

const result = await Unico.openSession({ url: processLink });
```
Native implementations required:
- **iOS**: `UnicoPlugin.swift` wrapping `ASWebAuthenticationSession`, registered via Capacitor
- **Android**: `UnicoPlugin.kt` with Custom Tabs + registration in `MainActivity.kt`
- **Both**: configure URL scheme in `capacitor.config`, `AndroidManifest.xml`, and `Info.plist`
## Step 5 — Retrieve the result (backend)
```bash
curl https://api.you.uat.unico.app/api/public/v1/process/{id} \
  -H "Authorization: Bearer $YOU_ACCESS_TOKEN"
```
Response:
```json
{
  "id": "a1b2c3d4-0000-0000-0000-abcdef012345",
  "status": "completed",
  "data": {
    "majority": {
      "value": "yes",
      "status": "validated"
    }
  }
}
```
### `majority.value` reference
| Value | Meaning |
|---|---|
| `yes` | User confirmed as 18 or older |
| `no` | User confirmed as under 18 |
| `inconclusive` | Age could not be determined |
### Process statuses
| Status | Meaning |
|---|---|
| `pending` | Process created, user has not started |
| `processing` | User completed the flow, results being processed |
| `completed` | Result is ready to retrieve |
| `expired` | Link expired before user completed the flow |
| `canceled` | Process was canceled |
## Step 6 — Apply the result to my application
```typescript
if (process.status !== "completed") {
  // handle non-terminal statuses (pending, processing, expired, canceled)
  return;
}

const majority = process.data.majority;

switch (majority.value) {
  case "yes":
    user.ageVerified = true;
    grantAccess();
    break;
  case "no":
    denyAccess();
    break;
  case "inconclusive":
    // decide per business rules: retry, deny, or manual review
    break;
}
```
## Error reference
| HTTP | Code | Message | Cause |
|---|---|---|---|
| 400 | 3 | `scopes is required` | `scopes` field missing |
| 400 | 3 | `scope "%s" invalid` | Unknown scope value |
| 400 | 3 | `branch id "%s" invalid` | Invalid `branchId` UUID |
| 401 | — | `Jwt is expired` | Token expired — re-authenticate |
| 401 | — | `failed to authorize request: no branch in request` | Invalid `branchId` |
| 429 | — | _(no body)_ | Rate limit — implement exponential back-off |
| 500 | 13 | `internal error` | — |
## Best-practice checklist before you mark this done
- [ ] Service account private key (`.pem`) and `YOU_BRANCH_ID` stored in secrets manager. Never committed.
- [ ] Backend token-exchange logic: RS256-signed JWT → `POST /oauth2/token` → cache for `expires_in - 600` seconds, then renew.
- [ ] Backend route that creates the process (no `person` or `scopeConfig` needed), persists `id`, and returns `token` + `link`.
- [ ] Web: `YouSDK.init()` called on page load, **before** process creation. HTTPS domain registered with Unico team.
- [ ] Native: `redirectUrl` URL scheme registered in `AndroidManifest.xml` intent-filter and `Info.plist`.
- [ ] Backend result-fetch endpoint calls `GET /api/public/v1/process/{id}` and reads `data.majority.value`.
- [ ] All three `majority.value` cases handled (`yes`, `no`, `inconclusive`).
- [ ] All five process statuses handled case-sensitively.
- [ ] No personal data stored — this flow is anonymous by design.
- [ ] Access token never sent to the browser.
## When you need more detail
- Authentication: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/authentication/obtaining-credentials`
- Getting started: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/you-api/api-reference/getting-started`
- Age verification flow: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/you-api/api-reference/age-verification-flow`
- Web SDK: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/web`
- Android: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/android/customtabs`
- iOS: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/ios/aswebauthenticationsession`
- Ionic: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/ionic`
- React Native: `https://devcenter.unico.io/you/Ol1H6eGO4sQvPYB1Nmyl/sdk/app/react-native`

Now build the integration. If anything in `## My application context` is missing, ask once at the top of your reply, then ship the complete change set: token-exchange logic, backend route to create the process, frontend SDK call or native link handling, result-fetch endpoint, and gate/access logic based on `majority.value`. Keep the access token server-side only, store no personal data, and handle all five process statuses case-sensitively.
````

{% endcode %}
