# KYC Requirements

Understand the document types and verifications each customer needs to complete in order to be approved.

## Verification Flow

When a customer reaches `pending_kyc` status, the `customer.kyc` array lists all required documents and verifications. The verification flow works in two steps:

1. **Early upload via API (optional):** the partner uploads documents they already have for the customer (identity, proof of address, company documents, etc.) through the API. This speeds up the process because those documents will already be ready when the customer starts the biometrics session.
2. **Biometrics session (required):** the partner generates a session URL and presents it to the customer. In the session, the customer submits remaining documents, takes a selfie, and confirms the submission. The confirmation is what triggers the evaluation of all documents.


The selfie can only be captured by the customer in the biometrics session.

> **Info:** The `kyc` array is only available from `pending_kyc` status onward. During the `creating` status, co-owner enrichment is still in progress and requirements have not been resolved yet.


## Requirement Structure

All requirements share the same base structure:


```json
{
  "requirement_id": "identity_12345678900",
  "requirement_type": "identity",
  "label": "Documento de identidade",
  "mandatory": true,
  "subject": {
    "name": "Joao Silva",
    "cpf": "12345678900",
    "subject_type": "applicant"
  },
  "submitted": null
}
```

| Field | Description |
|  --- | --- |
| `requirement_id` | Requirement identifier. Use this value when uploading documents. |
| `requirement_type` | Requirement type. Determines which evidence and attachments are accepted. |
| `label` | Human-readable requirement name for display to the user. |
| `mandatory` | If `true`, the requirement must be completed for the customer to proceed. |
| `subject` | Person or company the requirement refers to. `null` for company-wide requirements. |
| `submitted` | Submission state. `null` when no document has been submitted. |


## Person vs. Company Requirements

Requirements can be tied to a person (partner or applicant) or to the company as a whole.

Person requirements include the `subject` field with data about who must submit the document:


```json
"subject": {
  "name": "Joao Silva",
  "cpf": "12345678900",
  "subject_type": "applicant"
}
```

The `subject_type` can be:

- `applicant` for the registration holder
- `co_owner` for partners identified during enrichment


Company requirements (such as `company_document` or `articles_of_association`) do not have a `subject`. They apply to the registered company.

## Early Upload via API

If the partner already has the customer's documents, they can upload them through the API before the biometrics session. Documents uploaded early do not need to be submitted again in the session.

The `POST /v3/customers/{customer_id}/kyc-attachments` endpoint accepts individual uploads per requirement. Each upload requires the `requirement_id`, `evidence_type`, `attachment_type`, and the file.

> **Tip:** Early upload is optional, but reduces what the customer needs to do in the biometrics session. The more documents the partner uploads, the faster the customer completes verification.


## Requirement Types

### `identity` - Identity Document

Photo identity document for a person (applicant or co-owner). Accepts CNH or RG as evidence.

**CNH** (`evidence_type: "cnh"`): accepts two submission forms.

Physical CNH (front and back):

| Attachment | `attachment_type` | Formats |
|  --- | --- | --- |
| Front | `front` | JPEG, PNG |
| Back | `back` | JPEG, PNG |


Digital CNH (single file):

| Attachment | `attachment_type` | Formats |
|  --- | --- | --- |
| File | `file` | PDF |


**RG** (`evidence_type: "rg"`):

| Attachment | `attachment_type` | Formats |
|  --- | --- | --- |
| Front | `front` | JPEG, PNG |
| Back | `back` | JPEG, PNG |



```typescript Node.js
import fs from "fs";

// Physical CNH - upload front and back
await client.customers.uploadKycAttachment(customer.id, {
  requirementId: "identity_12345678900",
  evidenceType: "cnh",
  attachmentType: "front",
  file: fs.createReadStream("/path/to/cnh-front.jpg"),
});

await client.customers.uploadKycAttachment(customer.id, {
  requirementId: "identity_12345678900",
  evidenceType: "cnh",
  attachmentType: "back",
  file: fs.createReadStream("/path/to/cnh-back.jpg"),
});
```


```ruby Ruby
# Physical CNH - upload front and back
client.customers.upload_kyc_attachment(
  customer.id,
  requirement_id: "identity_12345678900",
  evidence_type: "cnh",
  attachment_type: "front",
  file: File.open("/path/to/cnh-front.jpg")
)

client.customers.upload_kyc_attachment(
  customer.id,
  requirement_id: "identity_12345678900",
  evidence_type: "cnh",
  attachment_type: "back",
  file: File.open("/path/to/cnh-back.jpg")
)
```


```python Python
# Physical CNH - upload front and back
client.customers.upload_kyc_attachment(
    customer.id,
    requirement_id="identity_12345678900",
    evidence_type="cnh",
    attachment_type="front",
    file=open("/path/to/cnh-front.jpg", "rb"),
)

client.customers.upload_kyc_attachment(
    customer.id,
    requirement_id="identity_12345678900",
    evidence_type="cnh",
    attachment_type="back",
    file=open("/path/to/cnh-back.jpg", "rb"),
)
```


```bash cURL
# Physical CNH - front
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/kyc-attachments" \
  -H "Authorization: Bearer dinie_at_..." \
  -F "requirement_id=identity_12345678900" \
  -F "evidence_type=cnh" \
  -F "attachment_type=front" \
  -F "file=@/path/to/cnh-front.jpg"

# Physical CNH - back
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/kyc-attachments" \
  -H "Authorization: Bearer dinie_at_..." \
  -F "requirement_id=identity_12345678900" \
  -F "evidence_type=cnh" \
  -F "attachment_type=back" \
  -F "file=@/path/to/cnh-back.jpg"
```

> **Tip:** The digital CNH is uploaded as a single PDF file, no need to separate front and back.


### `selfie` - Selfie

Photo of the person's face for biometric validation. Always tied to a `subject`.

| Attachment | `attachment_type` | Formats |
|  --- | --- | --- |
| Photo | `photo` | JPEG, PNG |


> **Warning:** The selfie can only be captured by the customer in the biometrics session. It cannot be uploaded through the API.


### `proof_of_address` - Proof of Address

Proof of address for the person. Always tied to a `subject`.

| Attachment | `attachment_type` | Formats |
|  --- | --- | --- |
| File | `file` | PDF, JPEG, PNG |



```typescript Node.js
await client.customers.uploadKycAttachment(customer.id, {
  requirementId: "proof_of_address_12345678900",
  evidenceType: "proof_of_address",
  attachmentType: "file",
  file: fs.createReadStream("/path/to/proof-of-address.pdf"),
});
```


```ruby Ruby
client.customers.upload_kyc_attachment(
  customer.id,
  requirement_id: "proof_of_address_12345678900",
  evidence_type: "proof_of_address",
  attachment_type: "file",
  file: File.open("/path/to/proof-of-address.pdf")
)
```


```python Python
client.customers.upload_kyc_attachment(
    customer.id,
    requirement_id="proof_of_address_12345678900",
    evidence_type="proof_of_address",
    attachment_type="file",
    file=open("/path/to/proof-of-address.pdf", "rb"),
)
```


```bash cURL
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/kyc-attachments" \
  -H "Authorization: Bearer dinie_at_..." \
  -F "requirement_id=proof_of_address_12345678900" \
  -F "evidence_type=proof_of_address" \
  -F "attachment_type=file" \
  -F "file=@/path/to/proof-of-address.pdf"
```

### `company_document` - Company Document

Company incorporation document. No `subject`, applies to the registered company.

| Attachment | `attachment_type` | Formats |
|  --- | --- | --- |
| File | `file` | PDF, JPEG, PNG |


The `evidence_type` is `ccmei` (Certificado da Condição de Microempreendedor Individual).


```typescript Node.js
await client.customers.uploadKycAttachment(customer.id, {
  requirementId: "company_document",
  evidenceType: "ccmei",
  attachmentType: "file",
  file: fs.createReadStream("/path/to/ccmei.pdf"),
});
```


```ruby Ruby
client.customers.upload_kyc_attachment(
  customer.id,
  requirement_id: "company_document",
  evidence_type: "ccmei",
  attachment_type: "file",
  file: File.open("/path/to/ccmei.pdf")
)
```


```python Python
client.customers.upload_kyc_attachment(
    customer.id,
    requirement_id="company_document",
    evidence_type="ccmei",
    attachment_type="file",
    file=open("/path/to/ccmei.pdf", "rb"),
)
```


```bash cURL
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/kyc-attachments" \
  -H "Authorization: Bearer dinie_at_..." \
  -F "requirement_id=company_document" \
  -F "evidence_type=ccmei" \
  -F "attachment_type=file" \
  -F "file=@/path/to/ccmei.pdf"
```

### `ei_mei_documents` - EI/MEI Documents

Composite requirement that requires two documents: the EI registration and the CCMEI. Both must be submitted. No `subject`.

| Attachment | `attachment_type` | Formats |
|  --- | --- | --- |
| EI Registration | `ei_registration_requirement` | PDF, JPEG, PNG |
| CCMEI | `ccmei` | PDF, JPEG, PNG |


The `evidence_type` for both is `ei_mei`.


```typescript Node.js
// EI Registration
await client.customers.uploadKycAttachment(customer.id, {
  requirementId: "ei_mei_documents",
  evidenceType: "ei_mei",
  attachmentType: "ei_registration_requirement",
  file: fs.createReadStream("/path/to/ei-registration.pdf"),
});

// CCMEI
await client.customers.uploadKycAttachment(customer.id, {
  requirementId: "ei_mei_documents",
  evidenceType: "ei_mei",
  attachmentType: "ccmei",
  file: fs.createReadStream("/path/to/ccmei.pdf"),
});
```


```ruby Ruby
# EI Registration
client.customers.upload_kyc_attachment(
  customer.id,
  requirement_id: "ei_mei_documents",
  evidence_type: "ei_mei",
  attachment_type: "ei_registration_requirement",
  file: File.open("/path/to/ei-registration.pdf")
)

# CCMEI
client.customers.upload_kyc_attachment(
  customer.id,
  requirement_id: "ei_mei_documents",
  evidence_type: "ei_mei",
  attachment_type: "ccmei",
  file: File.open("/path/to/ccmei.pdf")
)
```


```python Python
# EI Registration
client.customers.upload_kyc_attachment(
    customer.id,
    requirement_id="ei_mei_documents",
    evidence_type="ei_mei",
    attachment_type="ei_registration_requirement",
    file=open("/path/to/ei-registration.pdf", "rb"),
)

# CCMEI
client.customers.upload_kyc_attachment(
    customer.id,
    requirement_id="ei_mei_documents",
    evidence_type="ei_mei",
    attachment_type="ccmei",
    file=open("/path/to/ccmei.pdf", "rb"),
)
```


```bash cURL
# EI Registration
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/kyc-attachments" \
  -H "Authorization: Bearer dinie_at_..." \
  -F "requirement_id=ei_mei_documents" \
  -F "evidence_type=ei_mei" \
  -F "attachment_type=ei_registration_requirement" \
  -F "file=@/path/to/ei-registration.pdf"

# CCMEI
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/kyc-attachments" \
  -H "Authorization: Bearer dinie_at_..." \
  -F "requirement_id=ei_mei_documents" \
  -F "evidence_type=ei_mei" \
  -F "attachment_type=ccmei" \
  -F "file=@/path/to/ccmei.pdf"
```

### `income_statement` - Income Statement

Company income statement. No `subject`.

| Attachment | `attachment_type` | Formats |
|  --- | --- | --- |
| File | `file` | PDF, JPEG, PNG |



```typescript Node.js
await client.customers.uploadKycAttachment(customer.id, {
  requirementId: "income_statement",
  evidenceType: "income_statement",
  attachmentType: "file",
  file: fs.createReadStream("/path/to/income-statement.pdf"),
});
```


```ruby Ruby
client.customers.upload_kyc_attachment(
  customer.id,
  requirement_id: "income_statement",
  evidence_type: "income_statement",
  attachment_type: "file",
  file: File.open("/path/to/income-statement.pdf")
)
```


```python Python
client.customers.upload_kyc_attachment(
    customer.id,
    requirement_id="income_statement",
    evidence_type="income_statement",
    attachment_type="file",
    file=open("/path/to/income-statement.pdf", "rb"),
)
```


```bash cURL
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/kyc-attachments" \
  -H "Authorization: Bearer dinie_at_..." \
  -F "requirement_id=income_statement" \
  -F "evidence_type=income_statement" \
  -F "attachment_type=file" \
  -F "file=@/path/to/income-statement.pdf"
```

### `articles_of_association` - Articles of Association

Company's articles of association or equivalent document. No `subject`.

| Attachment | `attachment_type` | Formats |
|  --- | --- | --- |
| File | `file` | PDF, JPEG, PNG |



```typescript Node.js
await client.customers.uploadKycAttachment(customer.id, {
  requirementId: "articles_of_association",
  evidenceType: "articles_of_association",
  attachmentType: "file",
  file: fs.createReadStream("/path/to/articles-of-association.pdf"),
});
```


```ruby Ruby
client.customers.upload_kyc_attachment(
  customer.id,
  requirement_id: "articles_of_association",
  evidence_type: "articles_of_association",
  attachment_type: "file",
  file: File.open("/path/to/articles-of-association.pdf")
)
```


```python Python
client.customers.upload_kyc_attachment(
    customer.id,
    requirement_id="articles_of_association",
    evidence_type="articles_of_association",
    attachment_type="file",
    file=open("/path/to/articles-of-association.pdf", "rb"),
)
```


```bash cURL
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/kyc-attachments" \
  -H "Authorization: Bearer dinie_at_..." \
  -F "requirement_id=articles_of_association" \
  -F "evidence_type=articles_of_association" \
  -F "attachment_type=file" \
  -F "file=@/path/to/articles-of-association.pdf"
```

### `eireli_incorporation_statement` - EIRELI Incorporation Statement

Incorporation statement for an Empresa Individual de Responsabilidade Limitada. No `subject`.

| Attachment | `attachment_type` | Formats |
|  --- | --- | --- |
| File | `file` | PDF, JPEG, PNG |



```typescript Node.js
await client.customers.uploadKycAttachment(customer.id, {
  requirementId: "eireli_incorporation_statement",
  evidenceType: "eireli_incorporation_statement",
  attachmentType: "file",
  file: fs.createReadStream("/path/to/incorporation-statement.pdf"),
});
```


```ruby Ruby
client.customers.upload_kyc_attachment(
  customer.id,
  requirement_id: "eireli_incorporation_statement",
  evidence_type: "eireli_incorporation_statement",
  attachment_type: "file",
  file: File.open("/path/to/incorporation-statement.pdf")
)
```


```python Python
client.customers.upload_kyc_attachment(
    customer.id,
    requirement_id="eireli_incorporation_statement",
    evidence_type="eireli_incorporation_statement",
    attachment_type="file",
    file=open("/path/to/incorporation-statement.pdf", "rb"),
)
```


```bash cURL
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/kyc-attachments" \
  -H "Authorization: Bearer dinie_at_..." \
  -F "requirement_id=eireli_incorporation_statement" \
  -F "evidence_type=eireli_incorporation_statement" \
  -F "attachment_type=file" \
  -F "file=@/path/to/incorporation-statement.pdf"
```

### `email` - Co-owner Email

Person-level requirement for collecting the email address of co-owners who need to digitally sign the CCB. Each co-owner generates an `email_{cpf}` requirement with a `subject` field identifying the person.

> **Note:** The primary applicant's email is provided during customer creation. This requirement only appears for co-owners who need to sign the CCB.


Submission is done via the KYC attachment upload endpoint, using the `value` field instead of `file`:


```typescript Node.js
await client.customers.uploadKycAttachment(customer.id, {
  requirementId: "email_12345678900",
  evidenceType: "email",
  attachmentType: "email",
  value: "co-owner@example.com",
});
```


```ruby Ruby
client.customers.upload_kyc_attachment(
  customer.id,
  requirement_id: "email_12345678900",
  evidence_type: "email",
  attachment_type: "email",
  value: "co-owner@example.com"
)
```


```python Python
client.customers.upload_kyc_attachment(
    customer.id,
    requirement_id="email_12345678900",
    evidence_type="email",
    attachment_type="email",
    value="co-owner@example.com",
)
```


```bash cURL
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/kyc-attachments" \
  -H "Authorization: Bearer dinie_at_..." \
  -F "requirement_id=email_12345678900" \
  -F "evidence_type=email" \
  -F "attachment_type=email" \
  -F "value=co-owner@example.com"
```

## Biometrics Session

After uploading the documents they already have (or even without uploading any), the partner must generate a biometrics session and present the URL to the customer. In the session, the customer submits remaining documents, captures the selfie, and confirms the submission.

The confirmation in the biometrics session triggers the evaluation of all documents, including those uploaded early via the API.


```typescript Node.js
const session = await client.customers.createBiometricsSession(customer.id);

// Present this URL to the customer
console.log(session.session_url);
console.log(session.expires_at);  // Expires in 30 minutes
```


```ruby Ruby
session = client.customers.create_biometrics_session(customer.id)

# Present this URL to the customer
puts session.session_url
puts session.expires_at  # Expires in 30 minutes
```


```python Python
session = client.customers.create_biometrics_session(customer.id)

# Present this URL to the customer
print(session.session_url)
print(session.expires_at)  # Expires in 30 minutes
```


```bash cURL
curl -X POST "https://sandbox.api.dinie.com.br/v3/customers/${CUSTOMER_ID}/biometrics" \
  -H "Authorization: Bearer dinie_at_..."
```

> **Info:** The biometrics session expires in 30 minutes. Generate a new session if it times out.


## Review Status

After confirmation in the biometrics session, each requirement goes through review. The `submitted.review_status` field indicates the outcome:

| Status | Description |
|  --- | --- |
| `pending` | Document submitted, awaiting review |
| `accepted` | Document approved |
| `rejected` | Document rejected. Check `review_reason` for details |


When a document is rejected, the `submitted.review_reason` field contains the reason. Upload a new document for the same `requirement_id` to replace the previous one.

## Tracking Progress

The `customer.kyc_updated` webhook fires whenever a document is successfully submitted. The payload includes the updated `kyc` array with the state of all requirements.

When all mandatory requirements are accepted, the customer status advances to `under_review` and then to `active`.

## Quick Reference

| Requirement | `evidence_type` | Attachments | Formats | Subject | API Upload |
|  --- | --- | --- | --- | --- | --- |
| `identity` | `cnh` | `front`, `back` (physical) or `file` (digital) | JPEG, PNG or PDF | Person | Yes |
| `identity` | `rg` | `front`, `back` | JPEG, PNG | Person | Yes |
| `selfie` | `selfie` | `photo` | JPEG, PNG | Person | Biometrics only |
| `proof_of_address` | `proof_of_address` | `file` | PDF, JPEG, PNG | Person | Yes |
| `company_document` | `ccmei` | `file` | PDF, JPEG, PNG | Company | Yes |
| `ei_mei_documents` | `ei_mei` | `ei_registration_requirement`, `ccmei` | PDF, JPEG, PNG | Company | Yes |
| `income_statement` | `income_statement` | `file` | PDF, JPEG, PNG | Company | Yes |
| `articles_of_association` | `articles_of_association` | `file` | PDF, JPEG, PNG | Company | Yes |
| `eireli_incorporation_statement` | `eireli_incorporation_statement` | `file` | PDF, JPEG, PNG | Company | Yes |
| `email` | `email` | `email` | text (value) | Person | Yes |


## Related Guides

- [Registration and Offers](/en-us/guides/customer-registration) for the full onboarding flow
- [Webhooks](/en-us/apis/concepts/webhooks) to configure notification endpoints