> ## Documentation Index
> Fetch the complete documentation index at: https://docs.capcells.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Products

> The fields every configuration carries, what each Archetype adds and refuses, immutable versions, and the Platform's own bounds.

A Product configuration is the set of terms Loans are issued under: one Archetype, and the values that Archetype admits. It is created by `POST /v1/tenants/{tenant_id}/product-configurations`, which answers the stored configuration and the version it was written as.

A configuration is never edited. A change writes a new version, every earlier version stays readable, and a Loan keeps the version it was issued under.

A configuration names no jurisdiction and no borrower. It states the bounds a Loan's own figures fall inside: the principal range, the two rate corridors, the term menu, the fees and the cadence. Issuance picks one point inside each.

## Fields

The fields travel in `values`, and the Archetype's frame decides which of them a configuration admits. Every field is optional on the wire, and a missing one is refused by name rather than supplied.

Eight fields are required of all three Archetypes.

| Field                                  | What it fixes                                                                                                           |
| -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `principal_min`, `principal_max`       | The amounts a Loan's principal falls between.                                                                           |
| `rate_min`, `rate_max`                 | The corridor the Loan's ordinary rate is picked from, quoted annually.                                                  |
| `penalty_rate_min`, `penalty_rate_max` | The corridor the Loan's penalty rate is picked from, quoted annually.                                                   |
| `day_count_basis`                      | How interest counts days: `actual_360`, `actual_365_fixed` or `30e_360`.                                                |
| `schedule`                             | The cadence: `anchored_monthly`, `anchored_every_n_days` with `interval_days`, or `semi_monthly_grid` with `grid_days`. |

The penalty corridor is required of all three. A configuration that cannot say what happens when it is not paid does not describe a sellable product. A penalty rate invented at issuance is a term nobody agreed to.

Five fields are optional on all three Archetypes.

| Field                   | What it fixes                                             |
| ----------------------- | --------------------------------------------------------- |
| `origination_fee`       | A fee charged at issuance.                                |
| `first_period_gap_days` | Days between the anchor and the first due date.           |
| `late_fee`              | A flat charge once a Period stands unpaid past its grace. |
| `grace_days`            | Days after the due date before a Period counts overdue.   |
| `early_money_treatment` | What unelected money past every matured Period does.      |

An absent optional field means the absence rather than a default. No `late_fee` is no late fee, and no `grace_days` is no grace.

One of the five carries a meaning when absent. `early_money_treatment` absent is `reduces_principal`, the statutory prepayment: the law's answer rather than a value the Platform picked. Its alternative, `advances_instalments`, settles Periods that have not matured instead.

## Archetypes

An Archetype fixes the repayment shape. The set is closed — `instalment`, `bnpl` and `payday` — and no route adds a fourth.

The Archetype is chosen when the configuration is created, and a new version carries `values` alone. The shape a Loan was issued under never changes.

Each Archetype adds the fields its shape needs and locks the ones its shape fixes. A locked field is refused with the Archetype's own reason, and a field of another shape as not part of this frame. Neither is ignored: a value you believe is in force and is not is a dispute waiting for its Loan.

### Instalment

An `instalment` product repays a principal over a menu of terms, and each payment carries principal, interest, fee and tax. It locks nothing: a field this frame refuses is one belonging to another Archetype's shape.

Two fields belong to the instalment frame.

| Field                | Presence                              | What it fixes                                    |
| -------------------- | ------------------------------------- | ------------------------------------------------ |
| `terms`              | <Badge color="green">Required</Badge> | The menu of period counts a Loan is issued for.  |
| `minimum_instalment` | <Badge color="blue">Optional</Badge>  | The payment below which the product is not sold. |

An issuance whose quoted payment falls under `minimum_instalment` answers `422`. Nothing adjusts the term or the principal to clear the floor.

### Buy Now Pay Later

A `bnpl` product splits a purchase into instalments and always collects a payment at the purchase itself. That collection is the Archetype's own invariant, so a BNPL issuance carries a `down_payment`.

Four fields belong to the BNPL frame, and one of them exists to be refused.

| Field                     | Presence                              | What it fixes                                                     |
| ------------------------- | ------------------------------------- | ----------------------------------------------------------------- |
| `terms`                   | <Badge color="green">Required</Badge> | The menu of period counts a Loan is issued for.                   |
| `minimum_instalment`      | <Badge color="blue">Optional</Badge>  | The payment below which the product is not sold.                  |
| `merchant_commission_bps` | <Badge color="blue">Optional</Badge>  | The commission between tenant and store, in basis points, signed. |
| `payment_at_purchase`     | <Badge color="red">Locked</Badge>     | The collection at the purchase, which the Archetype fixes.        |

The commission is signed because it runs both ways. The store pays the tenant for financing the purchase, or the tenant pays the store for bringing the client. Nothing at issuance or in servicing reads it: the configuration records the commercial term, and settling it with the store is yours.

### Pay Day Loan

A `payday` product carries a single period to one date, and it is the only Archetype whose frame carries the rollover fields. It has no instalment menu, and `minimum_instalment` is not a field of a payday product.

Three fields belong to the payday frame.

| Field            | Presence                             | What it fixes                                              |
| ---------------- | ------------------------------------ | ---------------------------------------------------------- |
| `rollover_limit` | <Badge color="blue">Optional</Badge> | How often a Loan under this product rolls.                 |
| `rollover_fee`   | <Badge color="blue">Optional</Badge> | What one roll costs.                                       |
| `terms`          | <Badge color="red">Locked</Badge>    | The instalment menu, which a single period does not carry. |

Both rollover fields are recorded and read by nothing: no route rolls a Loan over, and none reports one.

## Example

The configuration behind every worked example on this site is an `instalment` product in MXN.

<CodeGroup>
  ```bash Create a configuration theme={null}
  curl -X POST "https://api.capcells.com/v1/tenants/$TENANT_ID/product-configurations" \
    -H "Authorization: Bearer $CAPCELLS_API_KEY" \
    -H "Content-Type: application/json" \
    -d '{
      "archetype": "instalment",
      "values": {
        "principal_min": { "minor_units": 100000, "currency": "MXN" },
        "principal_max": { "minor_units": 2000000, "currency": "MXN" },
        "rate_min": "36",
        "rate_max": "60",
        "penalty_rate_min": "36",
        "penalty_rate_max": "90",
        "day_count_basis": "actual_360",
        "schedule": { "kind": "anchored_monthly" },
        "terms": [6, 12, 18],
        "origination_fee": { "minor_units": 25000, "currency": "MXN" },
        "late_fee": { "minor_units": 15000, "currency": "MXN" }
      }
    }'
  ```

  ```json Response theme={null}
  {
    "id": "d57bfbfb-7703-4d87-8a0e-94d4200d3645",
    "tenant_id": "t-0001",
    "archetype": "instalment",
    "created_at": "2026-08-18T22:26:41.407349Z",
    "version": 1,
    "values": {
      "principal_min": { "minor_units": 100000, "currency": "MXN" },
      "principal_max": { "minor_units": 2000000, "currency": "MXN" },
      "rate_min": "36",
      "rate_max": "60",
      "penalty_rate_min": "36",
      "penalty_rate_max": "90",
      "day_count_basis": "actual_360",
      "origination_fee": { "minor_units": 25000, "currency": "MXN" },
      "late_fee": { "minor_units": 15000, "currency": "MXN" },
      "terms": [6, 12, 18],
      "schedule": { "kind": "anchored_monthly" }
    }
  }
  ```
</CodeGroup>

The answer is `201`. An issuance names this `id` as its `configuration_id` and this `version` as its `configuration_version`.

<Columns cols={2}>
  <Card title="Create a configuration" icon="terminal" href="/api-reference/product-configurations/create">
    The endpoint page: every field, every refusal, and a playground that sends the call.
  </Card>

  <Card title="Quickstart" icon="rocket" href="/getting-started/quickstart">
    The walk from this configuration to a settled instalment, in six calls.
  </Card>
</Columns>

## Versions

Five routes serve the configuration: they create it, list yours, read the latest version, write a new version, and read one version by number. The paths are on [API reference](/api-reference/overview), and not one of the five reads a query parameter.

Reading the latest and reading one version are separate addresses on purpose. `?version=2` on the read of the latest answers `400`: `this route does not read the parameter "version"`. A caller asking for a version is never handed whatever the newest happens to be.

A version number counts from 1 and rises by one. The word `latest` in an issuance's `configuration_version` is refused by name.

<Warning>
  A new version does not reach an issued Loan. Its Schedule, its amounts and its disclosed cost figure were priced at issuance, and nothing recomputes them.
</Warning>

## Limits

The Platform's own bounds are headroom, not policy. They catch a typo or an overflow. What is lawful to sell is your compliance question, and a configuration names no jurisdiction for the Platform to test it against.

| Bound                                      | Value                |
| ------------------------------------------ | -------------------- |
| Rate corridor, ordinary and penalty        | 0 to 2,000% per year |
| Merchant commission                        | −100% to 100%        |
| Term, in periods                           | 1 to 480             |
| Entries in the term menu                   | 24                   |
| `first_period_gap_days`                    | 0 to 365             |
| `rollover_limit`                           | 0 to 52              |
| `grace_days`                               | 0 to 90              |
| `interval_days` on `anchored_every_n_days` | 1 to 365             |

## Restrictions

Presence is read from the keys of `values`, not from what they decoded to. `"terms": []` and `"terms": null` both count as stated, so a locked field spelled empty is refused rather than quietly accepted.

<CodeGroup>
  ```json Request theme={null}
  {
    "archetype": "payday",
    "values": {
      "terms": []
    }
  }
  ```

  ```json Response 422 theme={null}
  {
    "error": "product: field is fixed by the archetype: terms on a payday product: a payday product is a single period and carries no instalment menu"
  }
  ```
</CodeGroup>

A configuration is submitted whole and rejected whole. The first failure stops the walk and names its field, so a body carrying two problems reports one. Fixing it reports the next.

These are the refusals, each `422`:

* a field the Archetype does not carry, and a field the Archetype locks, told apart by the message
* a required field the frame declares and the body omits
* a value outside a bound above
* a minimum above its maximum, in either corridor or in the principal range
* amounts in two currencies inside one configuration
* an empty term menu where the frame requires one, or the same term offered twice
* a rate quoted other than annually, or in a form that is not an exact decimal percentage
* a schedule carrying another kind's parameters: an interval on `anchored_monthly`, grid days on `anchored_every_n_days`
* a `semi_monthly_grid` of fewer than two days, of a day outside the month, or of a day repeated

A configuration belonging to another Tenancy answers `404`, the same as one that never existed. Any query parameter on any of the five routes answers `400`.
