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

# Payment intents

> The core primitive for creating and tracking payments

A Payment Intent represents a single payment request. It tracks the full lifecycle from creation through on-chain settlement.

## Schema

Each Payment Intent includes:

| Field       | Type           | Description                                                |
| ----------- | -------------- | ---------------------------------------------------------- |
| `id`        | string         | Unique ID, prefixed with `pi_`                             |
| `chain`     | string         | CAIP-2 chain identifier                                    |
| `token`     | string         | Canonical symbol (`USDC`) or chain-specific address        |
| `amount`    | string         | Decimal amount (e.g. `"10.50"`)                            |
| `requestor` | string         | Wallet address requesting payment                          |
| `fulfiller` | string \| null | Wallet address fulfilling payment                          |
| `direction` | string         | `request` (requestor created) or `send` (fulfiller pushes) |
| `state`     | string         | Current lifecycle state                                    |
| `reference` | string         | On-chain correlation key                                   |
| `metadata`  | object \| null | Custom key-value pairs (max 20 keys)                       |

See the [API reference](/api-reference/endpoint/create) for the complete schema.

## State machine

Payment Intents follow a strict state machine:

```
created ──→ pending ──→ confirmed ──→ finalized
  │            │            │
  │            │            └── (background finality)
  │            ├──→ failed
  │            └──→ expired*
  │
  └──→ cancelled
```

| State       | Meaning                                                                  |
| ----------- | ------------------------------------------------------------------------ |
| `created`   | Intent exists, no transaction submitted                                  |
| `pending`   | Signed transaction broadcast to chain, awaiting confirmation             |
| `confirmed` | Sufficient confirmations — **this is the user-facing completion signal** |
| `finalized` | Chain-level finality (background signal)                                 |
| `failed`    | On-chain failure (revert, program error)                                 |
| `expired`   | TTL elapsed without fulfillment                                          |
| `cancelled` | Explicitly cancelled before fulfillment                                  |

### Important behaviors

* **Expired with pending tx:** If a transaction is in the mempool when TTL expires, a 10-minute grace period is granted. If confirmed within grace, normal flow continues. Otherwise the intent is marked `expired` with `pendingAtExpiry: true`.
* **Dropped transactions (Solana):** Solana transactions expire after \~60 blockhashes (\~60s). If Crycket detects that a transaction was dropped, it resets the intent to `created` with `retryRequired: true`. The SDK surfaces a re-sign prompt.
* **Cancellation:** Only allowed from the `created` state, before any transaction is submitted.

## Direction

Payment Intents support two directions:

* **`request`** (default) — The requestor creates the intent and the fulfiller pays. This is the typical merchant or P2P "request money" flow.
* **`send`** — The fulfiller creates the intent and pushes payment. This is the "send money" flow.

## TTL and expiry

Every intent has a TTL (time-to-live):

* Default: 86,400 seconds (24 hours)
* Minimum: 300 seconds (5 minutes)
* Maximum: 2,592,000 seconds (30 days)

After the TTL elapses, unconfirmed intents transition to `expired`.

## Idempotency

Creating an intent with the same `idempotencyKey` within 24 hours returns the existing intent instead of creating a duplicate. The response includes `X-Idempotent-Replayed: true`.

## Refunds

You can create a linked refund intent by calling `POST /v1/intents/:id/refund`. The refund intent goes through the same lifecycle as a regular intent. The original intent's `refundedBy` field links to the refund, and the refund's `refundOf` field links back.
