---
title: codespar_kyc
description: Identity verification, or open a payments account. Persona, Sift, Konduto and Truora for checks; the licensed BaaS partner for onboarding, which verifies the consumer AND provisions the account that funds codespar_wallet.
---

import { Callout } from "fumadocs-ui/components/callout";

<MetaToolHeader tool="codespar_kyc" />

`check_type` selects the operation. `onboarding` and `onboarding-business` are special: they verify (background check plus documentoscopia) and provision a real payment account for the consumer, so that afterwards [`codespar_wallet`](/docs/concepts/meta-tools/wallet) and [`codespar_pay`](/docs/concepts/meta-tools/pay) work for the same `consumer_id`.

## Actions

<MetaToolActions tool="codespar_kyc" />

## Example

`check_type: "onboarding"` for a natural person (CPF). In sandbox, a `phoneNumber` ending in `1` auto-approves both gates.

<Split min={380}>
<SplitPane label="The same call, four ways">

<Tabs items={["MCP", "TypeScript", "Python", "CLI"]}>
<Tab value="MCP">

```json title="arguments"
{
  "name": "codespar_kyc",
  "arguments": {
    "check_type": "onboarding",
    "consumer_id": "consumer_0000",
    "buyer": {
      "fullName": "Example Person",
      "document": "00000000000",
      "email": "person@example.com",
      "phoneNumber": "+5500000000001",
      "birthDate": "01-01-2000",
      "motherName": "Example Parent",
      "address": {
        "postalCode": "00000000",
        "street": "Example St",
        "number": "0",
        "city": "Example",
        "state": "SP"
      },
      "country": "BR"
    }
  }
}
```

</Tab>
<Tab value="TypeScript">

```ts
const result = await session.execute("codespar_kyc", {
  check_type: "onboarding",
  consumer_id: "consumer_0000",
  buyer: {
    fullName: "Example Person",
    document: "00000000000",
    email: "person@example.com",
    phoneNumber: "+5500000000001",
    birthDate: "01-01-2000",
    motherName: "Example Parent",
    address: {
      postalCode: "00000000",
      street: "Example St",
      number: "0",
      city: "Example",
      state: "SP"
    },
    country: "BR"
  }
});
```

</Tab>
<Tab value="Python">

```python
result = session.execute("codespar_kyc", {
  "check_type": "onboarding",
  "consumer_id": "consumer_0000",
  "buyer": {
    "fullName": "Example Person",
    "document": "00000000000",
    "email": "person@example.com",
    "phoneNumber": "+5500000000001",
    "birthDate": "01-01-2000",
    "motherName": "Example Parent",
    "address": {
      "postalCode": "00000000",
      "street": "Example St",
      "number": "0",
      "city": "Example",
      "state": "SP"
    },
    "country": "BR"
  }
})
```

</Tab>
<Tab value="CLI">

```bash
codespar tool codespar_kyc \
  -i '{
       "check_type": "onboarding",
       "consumer_id": "consumer_0000",
       "buyer": {
         "fullName": "Example Person",
         "document": "00000000000",
         "email": "person@example.com",
         "phoneNumber": "+5500000000001",
         "birthDate": "01-01-2000",
         "motherName": "Example Parent",
         "address": {
           "postalCode": "00000000",
           "street": "Example St",
           "number": "0",
           "city": "Example",
           "state": "SP"
         },
         "country": "BR"
       }
     }'
```

</Tab>
</Tabs>

</SplitPane>
<SplitPane label="Result · fields the tool document names">

```json title="result"
{
  "verification_id": "ver_0000"
}
```

Every `check_type` returns a `verification_id`. Poll completion with `check_type: "status"`, passing that id and the same `document_number`; for onboarding the status is `pending`, `documentscopy_pending` (with a `hosted_url` where the consumer finishes document capture), `approved` (with the funding source) or `rejected`.

</SplitPane>
</Split>

## When to use

- **Before a consumer can hold funds in live.** `onboarding` (CPF) or `onboarding-business` (CNPJ: PJ or MEI) opens the payment account that becomes the wallet's funding source. For a business the documentoscopia target is the responsible partner, `buyer.owner[0]`, not the company. In the test environment the sandbox wallet ships pre-connected and no onboarding is needed.
- **As a gate before a high-value charge or payout**, or when a regulated workflow demands proof of identity: `identity` (document plus database check) or `risk-score` (behavioural fraud score).
- **To finish an onboarding**: poll `status` until it leaves `pending`; while it is `documentscopy_pending`, hand the consumer the `hosted_url`.

## Arguments

| Field | Type | Required | Description |
|---|---|---|---|
| `check_type` | `string` | Yes | `identity` \| `onboarding` \| `onboarding-business` \| `risk-score` \| `status` |
| `buyer` | `object` | Yes | The subject. Verification: `{ name, document, country, email }`. `onboarding`: `{ fullName, document (CPF), email, phoneNumber (+55…), birthDate (DD-MM-YYYY), motherName, address, country }`. `onboarding-business`: `{ document (CNPJ, 14 characters, digits or letters), businessName, tradingName?, businessEmail, contactNumber, businessAddress (or address), owner: [...] }`, where `owner[]` is required and its first entry is the responsible partner (`ownerType` `REPRESENTANTE`, full PF data; their CPF is the documentoscopia target) |
| `verification_id` | `string` | With `status` | From a prior call. It names the proposal that verified the document, and it is the only thing that provisions a payment account: a status poll without it never binds an account |
| `document_number` | `string` | With `status` | CPF (or CNPJ for an `onboarding-business` proposal). Identifies the subject; it must be the same document the proposal verified and is never used to look an account up |
| `consumer_id` | `string` | No | Whose account or verification. Defaults to the session user (onboarding and status) |
| `metadata` | `object` | No | Provider-specific overrides |

## Errors and what to do

| Error | Cause | What to do |
|---|---|---|
| `invalid_args` | `buyer` not an object, `check_type` missing or unknown. | Fix the call against the Arguments table. |
| `document_ownership_unproven` | A `status` poll with only a `document_number` and no `verification_id`. A document an agent typed is not proof it is the consumer's; nothing is provisioned. | Pass the `verification_id` the onboarding call returned. |
| `onboarding_document_mismatch` | The `document_number` on a `status` poll is not the document the `verification_id`'s proposal verified. | Poll with the same document you onboarded. |
| `rejected` | The provider refused the verification or the onboarding. | Surface it; a rejected onboarding does not open an account. |

Measured 2026-09-11: `codespar_kyc` takes no `action` at all. A call carrying one is refused with `invalid_args` before anything is dispatched, and the error names `check_type` as the discriminator. The undeclared `action: "status"` alias this note used to describe was removed in [ent#1185](https://github.com/codespar/codespar-enterprise/issues/1185), and [ent#1182](https://github.com/codespar/codespar-enterprise/issues/1182) is closed.

## Money and mandate

No money moves on this tool. `onboarding` and `onboarding-business` create the payment account the consumer's wallet is funded from; the account is bound only through a `verification_id`, never through a document number alone, so an agent cannot attach someone's account by typing their CPF. Later spends from that account run under the consumer's signed [mandate](/docs/concepts/directed-pay).

## Related

- [`codespar_wallet`](/docs/concepts/meta-tools/wallet): the wallet the onboarded account funds
- [`codespar_pay`](/docs/concepts/meta-tools/pay): `dda_subscribe` needs the document verified here
- [SDK reference, verificationStatus](/docs/api/sdk/status#verificationstatus)
- [Async settlement, the KYC sibling](/docs/api/reference/sessions#async-settlement) and [SSE streaming](/docs/api/reference/sessions#streaming-status)
- [Test mode](/docs/concepts/test-mode): the sandbox auto-approval rule

## Notes

**Rails, measured 2026-09-11 against the catalog.** `identity` routes to Persona (default; `inquiry_template_id` stamped per tenant in `connection_metadata`) or Truora; `risk-score` to Sift (INTL) or Konduto (BR fraud); `onboarding` and `onboarding-business` to the licensed BaaS partner. `status` reaches no provider at all: the dispatch answers it directly, reading the onboarding proposal and binding the funding source.

This paragraph previously said `document` routed to Persona and `sanctions` to Sift or Truora, carried forward as "previously documented". Neither has a line in the provider catalog, and the catalog is what the router intersects with your connections, so both came back `eligibility_empty` for every tenant. They are recorded as declared-and-unserved in the API repository ([ent#1280](https://github.com/codespar/codespar-enterprise/issues/1280)) and are no longer offered. For a document-only check use `identity`, which routes to the same providers.

**Operator setup.** Persona: API key, and stamp `inquiry_template_id` in `connection_metadata` when connecting (per tenant, not passed at execute time; without it inquiry creation fails; the dashboard's Persona connect modal has an input for it). Sift and Konduto: HTTP Basic auth (API key as username, blank password). Truora: API key.

**Tracking from the SDK, as previously documented.** `session.verificationStatus(tool_call_id)` and `session.verificationStatusStream(tool_call_id, opts)` follow the same correlation chain as payments (`idempotency_key` ↔ `external_reference` via webhook) and return `approved`, `rejected`, `review`, `expired` or `pending`, in that poll-priority order: when several events landed for one `tool_call_id`, the highest-priority terminal state wins. There is no typed wrapper for the check itself; `session.execute("codespar_kyc", arguments)` takes the same arguments as the MCP call.

**Result shape (SDK), as previously documented.** `data: { verification_id, status, hosted_url: string | null, check_type }`; for `onboarding` the create call returns `hosted_url: null` and the document-capture link is surfaced by `status`. This revision did not re-verify that list against the runtime.
