# Registration and Offers

Register customers, upload documentation, complete biometrics, and receive credit offers.

## Flow Overview

Two processes happen in parallel: the **credit analysis** (starts automatically upon registration) and **KYC verification** (the customer completes at their own pace).

![Customer lifecycle](/assets/customer-lifecycle.378b9e84279ac68d804612f1e785932f874d95bdbc1de2f378588f748b3d6b2c.047687d6.svg)

| Status | Description |
|  --- | --- |
| `creating` | Registration received, co-owner enrichment in progress |
| `pending_kyc` | Ready to submit documentation and biometrics |
| `under_review` | Documents submitted, analysis in progress |
| `active` | Verified and eligible for credit offers |
| `denied` | Permanently blocked |


## Create a Customer

Register the business with Dinie. The `external_id` field is the internal reference from your system that you will use to identify the customer.


```typescript Node.js
const customer = await client.customers.create({
  externalId: "partner-ref-123",      // Your internal ID
  cpf: "123.456.789-00",              // Business owner's CPF
  email: "joao@example.com",
  phone: "+5511999999999",
  cnpj: "12.345.678/0001-90",
});

console.log(customer.status); // "creating"
console.log(customer.kyc);    // Pending verification items
```


```ruby Ruby
customer = client.customers.create(
  external_id: "partner-ref-123",      # Your internal ID
  cpf: "123.456.789-00",              # Business owner's CPF
  email: "joao@example.com",
  phone: "+5511999999999",
  cnpj: "12.345.678/0001-90",
)

puts customer.status  # "creating"
puts customer.kyc     # Pending verification items
```


```python Python
customer = client.customers.create(
    external_id="partner-ref-123",      # Your internal ID
    cpf="123.456.789-00",              # Business owner's CPF
    email="joao@example.com",
    phone="+5511999999999",
    cnpj="12.345.678/0001-90",
)

print(customer.status)  # "creating"
print(customer.kyc)     # Pending verification items
```


```bash cURL
curl -X POST https://sandbox.api.dinie.com.br/v3/customers \
  -H "Authorization: Bearer dinie_at_..." \
  -H "Content-Type: application/json" \
  -d '{
    "external_id": "partner-ref-123",
    "cpf": "123.456.789-00",
    "email": "joao@example.com",
    "phone": "+5511999999999",
    "cnpj": "12.345.678/0001-90",
  }'
```

> **Info:** If you create a customer with the same CPF/CNPJ and `external_id`, the API returns the existing customer (idempotent). A `409` error only occurs when the `external_id` differs for the same CPF/CNPJ.


## Upload Documents

Upload the documents listed in the `customer.kyc` array right after registration to speed up the analysis:


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

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

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


```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")
)

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")
)
```


```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"),
)

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"),
)
```


```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"

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"
```

Supported formats: **PDF**, **JPEG**, **PNG**. Only upload the types listed in the `kyc` array -- unrecognized types return a `422` error.

> **Info:** Selfies cannot be uploaded via the API. They are captured by the customer during the [biometrics session](/en-us/guides/kyc-requirements#biometrics-session). See the [KYC Requirements](/en-us/guides/kyc-requirements) guide for details on the full flow.


## Receive the Credit Offer

The credit analysis starts automatically upon registration. When Dinie generates an offer, you receive the `credit_offer.available` webhook:


```typescript Node.js
app.post("/webhooks/dinie", express.raw({ type: "application/json" }), (req, res) => {
  const event = client.webhooks.unwrap(req.body.toString(), req.headers);

  if (event.type === "credit_offer.available") {
    const externalId = event.data.external_id;
    const amount = event.data.approved_amount;
    notifyCustomer(externalId, `Credit offer of R$ ${amount} available`);
  }

  res.sendStatus(200);
});
```


```ruby Ruby
post "/webhooks/dinie" do
  event = client.webhooks.unwrap(request.body.read, request.env)

  if event["type"] == "credit_offer.available"
    external_id = event.dig("data", "external_id")
    amount = event.dig("data", "approved_amount")
    notify_customer(external_id, "Credit offer of R$ #{amount} available")
  end

  status 200
end
```


```python Python
@app.post("/webhooks/dinie")
async def handle_webhook(request: Request):
    event = client.webhooks.unwrap(await request.body(), request.headers)

    if event["type"] == "credit_offer.available":
        external_id = event["data"]["external_id"]
        amount = event["data"]["approved_amount"]
        notify_customer(external_id, f"Credit offer of R$ {amount} available")

    return Response(status_code=200)
```


```bash cURL
# Webhooks are received via POST at your configured endpoint.
# Example credit_offer.available payload:
# {
#   "type": "credit_offer.available",
#   "data": {
#     "id": "co_550e8400...",
#     "customer_id": "cust_550e8400...",
#     "external_id": "partner-ref-123",
#     "approved_amount": 50000.00,
#     "interest_rate": 3.5
#   }
# }
```

## Next Step

When the customer has `active` status and an available offer, they are ready to proceed. Continue to the [Simulation and Loan Origination](/en-us/guides/credit-workflow) guide.