# Simulation and Loan Origination

Simulate installments, formalize the loan, sign the contract, and track through to disbursement.

## Flow Overview

From the moment the customer has a credit offer available, the flow goes through three steps on **your platform** (simulate, create, sign) while **Dinie** processes each phase and notifies via webhooks.

![Credit lifecycle](/assets/credit-lifecycle.a1e43c369a5c79f69948d37608104cb14ba59f5686b4dc79c97e3608d76b2c26.047687d6.svg)

| Status | Description |
|  --- | --- |
| `available` | Offer ready for simulation |
| `awaiting_signatures` | Loan created, awaiting CCB contract signatures |
| `processing` | All signatures collected, processing disbursement |
| `active` | Funds deposited, payment cycle started |
| `finished` | Loan fully paid |
| `cancelled` | Loan cancelled |
| `error` | Processing error |


## List Offers

When the customer opens the app to take out a loan, retrieve the available offers:


```typescript Node.js
const offers = await client.customers.listCreditOffers(
  "cust_550e8400...",
  { status: "available" }
);

const offer = offers.data[0];
console.log(`R$ ${offer.approvedAmount} at ${offer.interestRate}% per month`);
```


```ruby Ruby
offers = client.customers.list_credit_offers(
  "cust_550e8400...",
  status: "available"
)

offer = offers.data.first
puts "R$ #{offer.approved_amount} at #{offer.interest_rate}% per month"
```


```python Python
offers = client.customers.list_credit_offers(
    "cust_550e8400...",
    status="available",
)

offer = offers.data[0]
print(f"R$ {offer.approved_amount} at {offer.interest_rate}% per month")
```


```bash cURL
curl "https://sandbox.api.dinie.com.br/v3/customers/cust_550e8400.../credit-offers?status=available" \
  -H "Authorization: Bearer dinie_at_..."
```

## Simulate Terms

Simulate a loan scenario by providing the desired amount and number of installments. Each call returns a simulation with the calculated terms. The customer can simulate as many times as they want -- only the latest simulation is valid for loan origination.


```typescript Node.js
const simulation = await client.creditOffers.simulate(offer.id, {
  requestedAmount: 25000.00,
  installmentCount: 4,
});

console.log(`${simulation.installmentCount}x of R$ ${simulation.installmentAmount}`);
console.log(`Total: R$ ${simulation.totalAmount} | CET: ${simulation.cetRate}% p.a.`);
console.log(`First due date: ${simulation.firstDueDate}`);
```


```ruby Ruby
simulation = client.credit_offers.simulate(
  offer.id,
  requested_amount: 25000.00,
  installment_count: 4
)

puts "#{simulation.installment_count}x of R$ #{simulation.installment_amount}"
puts "Total: R$ #{simulation.total_amount} | CET: #{simulation.cet_rate}% p.a."
puts "First due date: #{simulation.first_due_date}"
```


```python Python
simulation = client.credit_offers.simulate(
    offer.id,
    requested_amount=25000.00,
    installment_count=4,
)

print(f"{simulation.installment_count}x of R$ {simulation.installment_amount}")
print(f"Total: R$ {simulation.total_amount} | CET: {simulation.cet_rate}% p.a.")
print(f"First due date: {simulation.first_due_date}")
```


```bash cURL
curl -X POST "https://sandbox.api.dinie.com.br/v3/credit-offers/${OFFER_ID}/simulations" \
  -H "Authorization: Bearer dinie_at_..." \
  -H "Content-Type: application/json" \
  -d '{
    "requested_amount": 25000.00,
    "installment_count": 4
  }'
```

The simulation returns `installment_amount`, `total_amount`, `requested_amount` (requested value), `principal_amount`, `iof_amount`, `fee_amount`, `interest_rate`, and `cet_rate` (annualized Total Effective Cost (CET)).

> **Info:** The `installment_count` field depends on the offer. Some offers allow the customer to choose between different terms -- in that case, you can simulate with different values. If the offer defines a fixed term, use the `installments` returned in the offer.


> **Tip:** Monetary values and rates are JSON numbers (e.g., `25000.00`), both in the SDKs and via HTTP directly.


## Create a Loan

When the customer chooses the desired simulation, formalize the loan. The `installment_count`, `installment_amount`, and `first_due_date` fields must match the referenced simulation exactly.


```typescript Node.js
const loan = await client.loans.create({
  creditOfferId: offer.id,
  simulationId: simulation.id,
  installmentCount: simulation.installmentCount,
  installmentAmount: simulation.installmentAmount,
  firstDueDate: simulation.firstDueDate,
});

console.log(loan.id);     // "ln_550e8400..."
console.log(loan.status); // "awaiting_signatures"
```


```ruby Ruby
loan = client.loans.create(
  credit_offer_id: offer.id,
  simulation_id: simulation.id,
  installment_count: simulation.installment_count,
  installment_amount: simulation.installment_amount,
  first_due_date: simulation.first_due_date
)

puts loan.id      # "ln_550e8400..."
puts loan.status  # "awaiting_signatures"
```


```python Python
loan = client.loans.create(
    credit_offer_id=offer.id,
    simulation_id=simulation.id,
    installment_count=simulation.installment_count,
    installment_amount=simulation.installment_amount,
    first_due_date=simulation.first_due_date,
)

print(loan.id)      # "ln_550e8400..."
print(loan.status)  # "awaiting_signatures"
```


```bash cURL
curl -X POST https://sandbox.api.dinie.com.br/v3/loans \
  -H "Authorization: Bearer dinie_at_..." \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: create-loan-${OFFER_ID}" \
  -d '{
    "credit_offer_id": "co_550e8400...",
    "simulation_id": "sim_550e8400...",
    "installment_count": 4,
    "installment_amount": 7997.34,
    "first_due_date": "2026-04-03"
  }'
```

> **Warning:** If `installment_count`, `installment_amount`, or `first_due_date` do not match the simulation values, the request is rejected with a `simulation_mismatch` error. Always use the exact values returned by the simulation.


## Sign the Contract

When the loan is created, the `signing_url` is immediately available in the response. Present it to the customer:


```typescript Node.js
// After creating the loan, the signing_url is available immediately:
const loan = await client.loans.create({ /* ... */ });

if (loan.signingUrl) {
  // Open loan.signingUrl in the customer's browser
  // The customer signs the CCB contract directly on this page
}
```


```ruby Ruby
# After creating the loan, the signing_url is available immediately:
loan = client.loans.create(# ...)

if loan.signing_url
  # Open loan.signing_url in the customer's browser
  # The customer signs the CCB contract directly on this page
end
```


```python Python
# After creating the loan, the signing_url is available immediately:
loan = client.loans.create(# ...)

if loan.signing_url:
    # Open loan.signing_url in the customer's browser
    # The customer signs the CCB contract directly on this page
    pass
```


```bash cURL
# The signing_url is included in the loan creation response
# Use it to redirect the customer to sign the CCB contract
```

After all signatures are collected, the loan advances automatically: `awaiting_signatures` -> `processing` -> `active`. You receive webhooks at each stage.


```typescript Node.js
switch (event.type) {
  case "loan.processing":
    notifyCustomer(event.data.id,
      "All signatures collected! We are processing the transfer.");
    break;

  case "loan.active":
    notifyCustomer(event.data.id,
      `R$ ${event.data.requested_amount} has been deposited into your account!`);
    break;
}
```


```ruby Ruby
case event["type"]
when "loan.processing"
  notify_customer(event.dig("data", "id"),
    "All signatures collected! We are processing the transfer.")
when "loan.active"
  notify_customer(event.dig("data", "id"),
    "R$ #{event.dig('data', 'requested_amount')} has been deposited into your account!")
end
```


```python Python
match event["type"]:
    case "loan.processing":
        notify_customer(event["data"]["id"],
            "All signatures collected! We are processing the transfer.")
    case "loan.active":
        notify_customer(event["data"]["id"],
            f"R$ {event['data']['requested_amount']} has been deposited into your account!")
```


```bash cURL
# Webhooks are received via POST at your configured endpoint.
# Example loan.processing payload:
# {
#   "type": "loan.processing",
#   "data": {
#     "id": "ln_550e8400...",
#     "status": "processing"
#   }
# }
```

## Next Step

When your integration is working in sandbox, follow the [Going to Production](/en-us/guides/going-to-production) checklist.