# Instant Checkout

Charge an amount that you decide at runtime instead of referencing a stored product or plan. Use it when pricing depends on the user (custom quotes, setup fees, usage roll-ups) or when the resource is short-lived and you don't want it cluttering your dashboard.&#x20;

For install and first render, see the [Quickstart](/sdk/quickstart). For the standard plan/product flow, see [Checkout](/sdk/checkout).

## When to pick instant over product/plan

* The **amount** isn't known until the user picks add-ons, quantities, or a quote lands.
* You charge something **once** and don't need it as a permanent record.
* You're building a marketplace where every seller has bespoke prices.

If the prices are stable and you want the resource to show up in the dashboard with historical reporting, create a regular product or plan and pass its ID instead.

## `instant-product` — one-time charge

A complete `instant-product` object describes a single, non-recurring charge:

| Field         | Type                   | Required | Notes                                                                                               |
| ------------- | ---------------------- | -------- | --------------------------------------------------------------------------------------------------- |
| `name`        | `{ language, text }[]` | Yes      | One entry per language. `language` is `'en' \| 'es' \| 'pt'`.                                       |
| `description` | `{ language, text }[]` | No       | Same shape as `name`.                                                                               |
| `amount`      | number                 | Yes      | Decimal in the **major unit** (`1990.00` = $1,990 ARS, `25.50` = $25.50 USD). Not cents.            |
| `currency`    | string                 | Yes      | ISO 4217. One of `ARS`, `BRL`, `CLP`, `COP`, `MXN`, `USD`.                                          |
| `metadata`    | object                 | No       | Arbitrary key-value pairs. Stored on the resulting payment; retrievable via `GET /v3/payments/:id`. |

### Embed with static JSON

```html
<rebill-checkout
  public-key="pk_your_public_key"
  instant-product='{
    "name": [{"language": "en", "text": "Premium Service"}],
    "description": [{"language": "en", "text": "Access for one year"}],
    "amount": 1990.00,
    "currency": "ARS",
    "metadata": {"userId": "12345", "campaignId": "summer2026"}
  }'
></rebill-checkout>
```

The attribute must be a JSON string. Inside the component it's parsed, and **`metadata` is stripped before the request** (the SDK keeps it client-side and re-attaches it to the resulting product/plan in the session response). Everything else is forwarded to `POST /v3/sessions/sdk`.

### Set dynamically from JavaScript

```javascript
import 'rebill'

const el = document.querySelector('rebill-checkout')
el.setAttribute('instant-product', JSON.stringify({
  name: [{ language: 'en', text: 'Custom Service Package' }],
  description: [{ language: 'en', text: 'Tier A + priority support' }],
  amount: 24.99,
  currency: 'USD',
  metadata: { selectedTier: 'A', support: 'priority' }
}))
```

## `instant-plan` — runtime-priced subscription

Use `instant-plan` when the subscription's price or cadence changes per customer:

| Field         | Type                   | Required | Notes                                              |
| ------------- | ---------------------- | -------- | -------------------------------------------------- |
| `name`        | `{ language, text }[]` | Yes      |                                                    |
| `description` | `{ language, text }[]` | No       |                                                    |
| `amount`      | number                 | Yes      | Decimal major unit. The amount charged each cycle. |
| `currency`    | string                 | Yes      | ISO 4217.                                          |
| `plan`        | `PlanConfig`           | Yes      | Cadence + lifecycle, see below.                    |
| `metadata`    | object                 | No       | Attached to the subscription.                      |

The nested `plan` object:

| Field              | Type           | Required | Notes                                                                                                                  |
| ------------------ | -------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
| `frequency.period` | string         | Yes      | `'minute' \| 'hour' \| 'day' \| 'month' \| 'year'`. Shorter cadences are for testing only.                             |
| `frequency.count`  | number         | Yes      | `1` with `period: "month"` bills monthly. `3` with `period: "month"` bills every three months.                         |
| `repetitions`      | number \| null | No       | Total billing cycles before the subscription ends. Minimum `2` when set. Omit or set to `null` for open-ended renewal. |
| `type`             | string         | No       | `'units' \| 'usage'`. Defaults to `units`. Use `usage` only when billing is metered (consumption reports).             |
| `metadata`         | object         | No       |                                                                                                                        |

```html
<rebill-checkout
  public-key="pk_your_public_key"
  instant-plan='{
    "name": [{"language": "en", "text": "Monthly Premium"}],
    "description": [{"language": "en", "text": "Billed monthly, cancel anytime"}],
    "amount": 14500.00,
    "currency": "ARS",
    "plan": {
      "frequency": { "period": "month", "count": 1 },
      "repetitions": null,
      "metadata": { "tier": "premium" }
    }
  }'
></rebill-checkout>
```

## Dynamic pricing pattern

A common shape: compute the amount from the customer's selection, serialize it, and hand it to the component before it renders.

```javascript
import 'rebill'

function buildInstantProduct(selections) {
  const basePrice = 19.90
  const addons = selections.includes('priority-support') ? 5.00 : 0
  const total = basePrice + addons
  return {
    name: [{ language: 'en', text: 'Custom Package' }],
    description: [{ language: 'en', text: `Includes: ${selections.join(', ')}` }],
    amount: Number(total.toFixed(2)),
    currency: 'USD',
    metadata: { selections: selections.join(',') }
  }
}

const el = document.querySelector('rebill-checkout')
el.setAttribute('instant-product', JSON.stringify(buildInstantProduct(['priority-support'])))
```

Set the attribute **before** the customer interacts with the form. Changing `instant-product` after the session has been created requires re-mounting the component.

## Common mistakes

* **Treating `amount` as cents.** `amount: 1990` means $1,990, not $19.90. Use decimals: `19.90`.
* **Forgetting `currency`.** Without it the session creation returns `400 currency must be a valid enum value`.
* **Sending `metadata` with non-string values hoping for filtering.** Metadata round-trips through JSON — the payment-service stores what you send, but filtering on nested objects isn't supported. Keep values flat strings or numbers.
* **Assuming `instant-plan` replaces `plan-id` forever.** The resulting subscription is real; it appears in the dashboard and renews on schedule. "Instant" refers to creation, not lifetime.
* **Using `period: "minute"` or `"hour"` in production.** Those cadences exist for testing subscription retries. Use `day`, `month`, or `year` for live customers.

## Next

* [Checkout](/sdk/checkout) — the stored-resource flow (`product-id` / `plan-id`).
* [Customization](/sdk/customization) — `display`, `css`, `one-click-checkout`, and the rest of the attribute list.
* [Test payment methods](/sdk/test-payment-methods) — cards and outcomes for sandbox runs.
