# Event types (/developers/webhooks/events)

Fianto sends 14 event types, plus `endpoint.verification` for checking your URL. Every event has the
envelope `{ id, type, timestamp, data }`; this page shows each one in full. `data` for each type is
defined by the `webhooks` section of Fianto's OpenAPI document. The ids, amounts and dates below are
examples.

## Reading the payloads [#reading-the-payloads]

* **Amounts** (`amount`, `fee_amount`, `total_amount`, `duplicate.amount`, `period.amount_due`,
  `period.fee_due`) are strings in USDC base units, with 6 decimals: `"25000000"` is 25 USDC. The SDK's
  `usdc.format("25000000")` gives `"25.00 USDC"`.
* **Fees in these examples are placeholders.** The `fee_amount` and `fee_due` values below are for
  illustration only; they are not Fianto's fee.
* **Ids** carry their type in the prefix: `fian_cs_` checkout session, `fian_ord_` order, `fian_pay_`
  payment, `fian_cus_` customer, `fian_sub_` subscription, `fian_plan_` on-chain plan, `fian_price_`
  price, `evt_` event. `order_id` is your own id.
* **Statuses** are UPPERCASE; `mode` is lowercase.
* **Absent values are `null`**, not missing keys. `period` and `failure` appear only on the
  subscription events that say so.
* **`customer.id` is `null` until a payment settles.** On a session that expired or was cancelled,
  `customer.wallet` is the wallet that prepared a transaction on the checkout page, or `null` when
  none did. An expired order's `customer.id` and `customer.wallet` are `null`.

> **What the payer paid:**
>
> `amount` is your price. `fee_amount` is Fianto's service fee, which the payer pays on top when it is
> above zero. `total_amount` is `amount` plus `fee_amount`: the USDC the payer paid. The Solana network
> fee, and on a subscribe the account rent, are separate and paid in SOL. The fee figures on this
> page are placeholders, not Fianto's fee.

Handle every type you do not recognise by answering `2xx`: Fianto may add event types later.

## Checkout sessions [#checkout-sessions]

These three use the same checkout session object. In a subscription session, `mode` is
`subscription`, `interval` is set, and `subscription` names the subscription once it exists.

### `checkout.session.completed`

The session's payment finalized while the session was still OPEN. Sent for payment and subscription sessions; the payer saw success earlier, at confirmed. A payment that finalizes after its session expired or was cancelled does not send it.

```json
{
  "id": "evt_227f61d4801bb92e65e33c0bae85b244",
  "type": "checkout.session.completed",
  "timestamp": "2026-09-28T10:02:14.000Z",
  "data": {
    "id": "fian_cs_1K9jcgxlKycOIAfvD0RL2P3PSblw",
    "object": "checkout_session",
    "mode": "payment",
    "status": "COMPLETED",
    "order_id": "order_1001",
    "amount": "25000000",
    "fee_amount": "250000",
    "total_amount": "25250000",
    "currency": "USDC",
    "interval": null,
    "subscription": null,
    "customer": {
      "id": "fian_cus_0cJCFTfDw8ZkPLbdr7m2d62PU0SL",
      "wallet": "FxLsWPrqTpmc1yHrKoYj8SE75mAzavKwPDen7Y2xF7pw",
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "payment": {
      "id": "fian_pay_5caFRGUqMblmAgHXQ22uKn0hcZSj",
      "signature": "dbmKAFCAkMz8B2QTsHWkF1GtGVWQkjqXszoGh3XqDsyqsE26ftxJBSAeCpymBVfFGAjA8k6wG52JoKUcwo4nyMN",
      "status": "SUCCEEDED"
    },
    "metadata": {
      "cart": "c_881"
    },
    "expires_at": "2026-09-28T10:30:00.000Z",
    "created_at": "2026-09-28T10:00:00.000Z",
    "completed_at": "2026-09-28T10:02:14.000Z",
    "expired_at": null,
    "canceled_at": null
  }
}
```

### `checkout.session.expired`

Nobody paid before expires_at. Fianto's expiry check (every 30 seconds) expired the session, or a new create for the same order_id replaced the lapsed session. A payment in flight keeps a session from expiring.

```json
{
  "id": "evt_c9a125b2741eff78036b22f90bddd07f",
  "type": "checkout.session.expired",
  "timestamp": "2026-09-28T10:30:21.000Z",
  "data": {
    "id": "fian_cs_1K9jcgxlKycOIAfvD0RL2P3PSblw",
    "object": "checkout_session",
    "mode": "payment",
    "status": "EXPIRED",
    "order_id": "order_1001",
    "amount": "25000000",
    "fee_amount": "250000",
    "total_amount": "25250000",
    "currency": "USDC",
    "interval": null,
    "subscription": null,
    "customer": {
      "id": null,
      "wallet": null,
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "payment": {
      "id": null,
      "signature": null,
      "status": null
    },
    "metadata": {
      "cart": "c_881"
    },
    "expires_at": "2026-09-28T10:30:00.000Z",
    "created_at": "2026-09-28T10:00:00.000Z",
    "completed_at": null,
    "expired_at": "2026-09-28T10:30:21.000Z",
    "canceled_at": null
  }
}
```

### `checkout.session.canceled`

You cancelled the session with POST /v1/checkout-sessions/{id}/cancel, or the payer cancelled on the checkout page. Fianto also cancels a subscription session whose link is out of date because your wallet or Fianto's treasury changed since it was created; the payer sees checkout_link_outdated. No order event is sent; in payment mode the order stays PENDING.

```json
{
  "id": "evt_32f7ffebb3d612d37358b990889026a1",
  "type": "checkout.session.canceled",
  "timestamp": "2026-09-28T10:04:09.000Z",
  "data": {
    "id": "fian_cs_1K9jcgxlKycOIAfvD0RL2P3PSblw",
    "object": "checkout_session",
    "mode": "payment",
    "status": "CANCELED",
    "order_id": "order_1001",
    "amount": "25000000",
    "fee_amount": "250000",
    "total_amount": "25250000",
    "currency": "USDC",
    "interval": null,
    "subscription": null,
    "customer": {
      "id": null,
      "wallet": null,
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "payment": {
      "id": null,
      "signature": null,
      "status": null
    },
    "metadata": {
      "cart": "c_881"
    },
    "expires_at": "2026-09-28T10:30:00.000Z",
    "created_at": "2026-09-28T10:00:00.000Z",
    "completed_at": null,
    "expired_at": null,
    "canceled_at": "2026-09-28T10:04:09.000Z"
  }
}
```

## Orders [#orders]

Orders exist only for one-time payments (`mode: payment`).

### `order.paid`

The order's payment finalized: the order is PAID and the payment SUCCEEDED. Fulfil the order here. Also sent when a payment for an expired or cancelled session still finalizes, unless you have opened a newer session for the order since.

```json
{
  "id": "evt_25eaf6aa00802f4e758d9dbfb332941a",
  "type": "order.paid",
  "timestamp": "2026-09-28T10:02:14.000Z",
  "data": {
    "id": "fian_ord_4zXwxAivgnEyzWwFpTvsTp16x3OZ",
    "object": "order",
    "order_id": "order_1001",
    "checkout_session_id": "fian_cs_1K9jcgxlKycOIAfvD0RL2P3PSblw",
    "status": "PAID",
    "amount": "25000000",
    "fee_amount": "250000",
    "total_amount": "25250000",
    "currency": "USDC",
    "customer": {
      "id": "fian_cus_0cJCFTfDw8ZkPLbdr7m2d62PU0SL",
      "wallet": "FxLsWPrqTpmc1yHrKoYj8SE75mAzavKwPDen7Y2xF7pw",
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "payment": {
      "id": "fian_pay_5caFRGUqMblmAgHXQ22uKn0hcZSj",
      "signature": "dbmKAFCAkMz8B2QTsHWkF1GtGVWQkjqXszoGh3XqDsyqsE26ftxJBSAeCpymBVfFGAjA8k6wG52JoKUcwo4nyMN"
    },
    "metadata": {
      "cart": "c_881"
    },
    "created_at": "2026-09-28T10:00:00.000Z",
    "paid_at": "2026-09-28T10:02:14.000Z",
    "expired_at": null
  }
}
```

> **Fulfil on order.paid, not earlier:**
>
> The payer's checkout page shows success at confirmed, before the order is paid. `order.paid` comes
> only at finalized. An order that is `EXPIRED` is not final either: it can still become `PAID`.

### `order.expired`

Sent with checkout.session.expired when Fianto's expiry check expires the order's session. Not sent when a new create for the same order_id replaced the lapsed session: the order is reopened instead. An EXPIRED order can still become PAID if a late payment finalizes.

```json
{
  "id": "evt_a50d09fe1700e019e00a05b9f5287263",
  "type": "order.expired",
  "timestamp": "2026-09-28T10:30:21.000Z",
  "data": {
    "id": "fian_ord_4zXwxAivgnEyzWwFpTvsTp16x3OZ",
    "object": "order",
    "order_id": "order_1001",
    "checkout_session_id": "fian_cs_1K9jcgxlKycOIAfvD0RL2P3PSblw",
    "status": "EXPIRED",
    "amount": "25000000",
    "fee_amount": "250000",
    "total_amount": "25250000",
    "currency": "USDC",
    "customer": {
      "id": null,
      "wallet": null,
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "payment": {
      "id": null,
      "signature": null
    },
    "metadata": {
      "cart": "c_881"
    },
    "created_at": "2026-09-28T10:00:00.000Z",
    "paid_at": null,
    "expired_at": "2026-09-28T10:30:21.000Z"
  }
}
```

### `order.duplicate_payment`

A transfer carrying the session's reference arrived that is not the order's own payment, while the order was already PAID or another payment for it was under way. duplicate names that transfer's signature and amount. Other stray transfers are recorded on the order without a webhook.

```json
{
  "id": "evt_977902996383ed2ca3ee2951991d7070",
  "type": "order.duplicate_payment",
  "timestamp": "2026-09-28T10:06:40.000Z",
  "data": {
    "id": "fian_ord_4zXwxAivgnEyzWwFpTvsTp16x3OZ",
    "object": "order",
    "order_id": "order_1001",
    "checkout_session_id": "fian_cs_1K9jcgxlKycOIAfvD0RL2P3PSblw",
    "status": "PAID",
    "amount": "25000000",
    "fee_amount": "250000",
    "total_amount": "25250000",
    "currency": "USDC",
    "customer": {
      "id": "fian_cus_0cJCFTfDw8ZkPLbdr7m2d62PU0SL",
      "wallet": "FxLsWPrqTpmc1yHrKoYj8SE75mAzavKwPDen7Y2xF7pw",
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "payment": {
      "id": "fian_pay_5caFRGUqMblmAgHXQ22uKn0hcZSj",
      "signature": "dbmKAFCAkMz8B2QTsHWkF1GtGVWQkjqXszoGh3XqDsyqsE26ftxJBSAeCpymBVfFGAjA8k6wG52JoKUcwo4nyMN"
    },
    "metadata": {
      "cart": "c_881"
    },
    "created_at": "2026-09-28T10:00:00.000Z",
    "paid_at": "2026-09-28T10:02:14.000Z",
    "expired_at": null,
    "duplicate": {
      "signature": "yKGVjQGUX1vNVsmDXJEXzWZ6PTJzuybo5Bi7GtVGyu5KQtPqfeBxRam9jZJv4cphmcLHSw14akfnsT9Jov38wn5",
      "amount": "25000000"
    }
  }
}
```

> **The payer paid twice:**
>
> The duplicate transfer's USDC is in your wallet. There is no refund API: to give it back, send it
> from your wallet yourself.

## Subscriptions [#subscriptions]

The seven subscription events share one subscription object. `interval` is `MONTH` (every 30 days,
`period_hours: 720`) or `YEAR` (every 365 days). `customer.wallet` is always set.

### `subscription.created`

The subscribe transaction finalized. The subscription starts ACTIVE with its first period already paid: period describes that period and payment is the subscribe payment. The session also sends checkout.session.completed if it was still OPEN.

```json
{
  "id": "evt_83ec8108fd794c1da842ba575bf48e61",
  "type": "subscription.created",
  "timestamp": "2026-09-28T10:00:31.000Z",
  "data": {
    "id": "fian_sub_1543enRaYWeBHUQnvfZU4M4He6NO",
    "object": "subscription",
    "status": "ACTIVE",
    "order_id": "sub_user_42_pro",
    "price_id": "fian_price_2NCT7hYJqQnFadrhpQLHC50tjj6Z",
    "plan_id": "fian_plan_1vRG2vTS0AW2RvlHnpHoAm3CMuqn",
    "product_name": "Pro plan",
    "amount": "10000000",
    "fee_amount": "100000",
    "total_amount": "10100000",
    "currency": "USDC",
    "interval": "MONTH",
    "period_hours": 720,
    "customer": {
      "id": "fian_cus_0cJCFTfDw8ZkPLbdr7m2d62PU0SL",
      "wallet": "FxLsWPrqTpmc1yHrKoYj8SE75mAzavKwPDen7Y2xF7pw",
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "current_period_index": 0,
    "current_period_start": "2026-09-28T10:00:00.000Z",
    "current_period_end": "2026-10-28T10:00:00.000Z",
    "cancel_at_period_end": false,
    "cancel_at": null,
    "cancel_reason": null,
    "merchant_cancel_requested": false,
    "end_reason": null,
    "started_at": "2026-09-28T10:00:00.000Z",
    "ended_at": null,
    "created_at": "2026-09-28T10:00:00.000Z",
    "payment": {
      "id": "fian_pay_1julpAQPFgoyXMFPOySxNG1gfJ6b",
      "status": "SUCCEEDED",
      "signature": "237o8YbZpUMvK9tEQyGU894yJB1fprp4ETw2zqoZ2fMHw5MR5fFRodksYz7bmuMpdw6WWNgArzEAHmeP9WP6cRV6"
    },
    "metadata": {
      "plan": "pro"
    },
    "period": {
      "index": 0,
      "start": "2026-09-28T10:00:00.000Z",
      "end": "2026-10-28T10:00:00.000Z",
      "amount_due": "10000000",
      "fee_due": "100000",
      "status": "PAID"
    }
  }
}
```

### `subscription.renewed`

A renewal charge finalized. period is the period it paid and payment is that charge. A PAST_DUE subscription is ACTIVE again.

```json
{
  "id": "evt_e26bb0554992eb494bec6ac8674decfb",
  "type": "subscription.renewed",
  "timestamp": "2026-10-28T10:02:40.000Z",
  "data": {
    "id": "fian_sub_1543enRaYWeBHUQnvfZU4M4He6NO",
    "object": "subscription",
    "status": "ACTIVE",
    "order_id": "sub_user_42_pro",
    "price_id": "fian_price_2NCT7hYJqQnFadrhpQLHC50tjj6Z",
    "plan_id": "fian_plan_1vRG2vTS0AW2RvlHnpHoAm3CMuqn",
    "product_name": "Pro plan",
    "amount": "10000000",
    "fee_amount": "100000",
    "total_amount": "10100000",
    "currency": "USDC",
    "interval": "MONTH",
    "period_hours": 720,
    "customer": {
      "id": "fian_cus_0cJCFTfDw8ZkPLbdr7m2d62PU0SL",
      "wallet": "FxLsWPrqTpmc1yHrKoYj8SE75mAzavKwPDen7Y2xF7pw",
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "current_period_index": 1,
    "current_period_start": "2026-10-28T10:00:00.000Z",
    "current_period_end": "2026-11-27T10:00:00.000Z",
    "cancel_at_period_end": false,
    "cancel_at": null,
    "cancel_reason": null,
    "merchant_cancel_requested": false,
    "end_reason": null,
    "started_at": "2026-09-28T10:00:00.000Z",
    "ended_at": null,
    "created_at": "2026-09-28T10:00:00.000Z",
    "payment": {
      "id": "fian_pay_6jsJFde77WW4tOHiaJG7UJ3yTiEY",
      "status": "SUCCEEDED",
      "signature": "3eEw7VXVXdLLjXGCDoe2TzjSr6N7C5gYPFpyVGdB5HxTvUxBEia3nfTERAB8cLwjW24yC6k4tcKC63paf8YMK5sH"
    },
    "metadata": {
      "plan": "pro"
    },
    "period": {
      "index": 1,
      "start": "2026-10-28T10:00:00.000Z",
      "end": "2026-11-27T10:00:00.000Z",
      "amount_due": "10000000",
      "fee_due": "100000",
      "status": "PAID"
    }
  }
}
```

### `subscription.payment_failed`

Sent on every counted renewal failure: INSUFFICIENT_FUNDS, DELEGATION_REVOKED, ACCOUNT_FROZEN or ACCOUNT_CLOSED. failure gives the reason, how many counted failures the period has had, and the next attempt (null when none is left). payment is the failed charge, or all null when the failure was found before anything was sent.

```json
{
  "id": "evt_61d60a317212139638347d537f9485b3",
  "type": "subscription.payment_failed",
  "timestamp": "2026-10-28T10:02:05.000Z",
  "data": {
    "id": "fian_sub_1543enRaYWeBHUQnvfZU4M4He6NO",
    "object": "subscription",
    "status": "PAST_DUE",
    "order_id": "sub_user_42_pro",
    "price_id": "fian_price_2NCT7hYJqQnFadrhpQLHC50tjj6Z",
    "plan_id": "fian_plan_1vRG2vTS0AW2RvlHnpHoAm3CMuqn",
    "product_name": "Pro plan",
    "amount": "10000000",
    "fee_amount": "100000",
    "total_amount": "10100000",
    "currency": "USDC",
    "interval": "MONTH",
    "period_hours": 720,
    "customer": {
      "id": "fian_cus_0cJCFTfDw8ZkPLbdr7m2d62PU0SL",
      "wallet": "FxLsWPrqTpmc1yHrKoYj8SE75mAzavKwPDen7Y2xF7pw",
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "current_period_index": 0,
    "current_period_start": "2026-09-28T10:00:00.000Z",
    "current_period_end": "2026-10-28T10:00:00.000Z",
    "cancel_at_period_end": false,
    "cancel_at": null,
    "cancel_reason": null,
    "merchant_cancel_requested": false,
    "end_reason": null,
    "started_at": "2026-09-28T10:00:00.000Z",
    "ended_at": null,
    "created_at": "2026-09-28T10:00:00.000Z",
    "payment": {
      "id": null,
      "signature": null,
      "status": null
    },
    "metadata": {
      "plan": "pro"
    },
    "failure": {
      "reason": "INSUFFICIENT_FUNDS",
      "attempt_no": 1,
      "next_attempt_at": "2026-10-28T11:00:00.000Z"
    }
  }
}
```

### `subscription.past_due`

The first counted failure while the subscription was ACTIVE, with attempts left: it is now PAST_DUE. Sent once, alongside subscription.payment_failed, with the same failure. Fianto keeps retrying; a successful renewal makes it ACTIVE again.

```json
{
  "id": "evt_0cdbd8eaea8b6f9c6f0e69d846353b14",
  "type": "subscription.past_due",
  "timestamp": "2026-10-28T10:02:05.000Z",
  "data": {
    "id": "fian_sub_1543enRaYWeBHUQnvfZU4M4He6NO",
    "object": "subscription",
    "status": "PAST_DUE",
    "order_id": "sub_user_42_pro",
    "price_id": "fian_price_2NCT7hYJqQnFadrhpQLHC50tjj6Z",
    "plan_id": "fian_plan_1vRG2vTS0AW2RvlHnpHoAm3CMuqn",
    "product_name": "Pro plan",
    "amount": "10000000",
    "fee_amount": "100000",
    "total_amount": "10100000",
    "currency": "USDC",
    "interval": "MONTH",
    "period_hours": 720,
    "customer": {
      "id": "fian_cus_0cJCFTfDw8ZkPLbdr7m2d62PU0SL",
      "wallet": "FxLsWPrqTpmc1yHrKoYj8SE75mAzavKwPDen7Y2xF7pw",
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "current_period_index": 0,
    "current_period_start": "2026-09-28T10:00:00.000Z",
    "current_period_end": "2026-10-28T10:00:00.000Z",
    "cancel_at_period_end": false,
    "cancel_at": null,
    "cancel_reason": null,
    "merchant_cancel_requested": false,
    "end_reason": null,
    "started_at": "2026-09-28T10:00:00.000Z",
    "ended_at": null,
    "created_at": "2026-09-28T10:00:00.000Z",
    "payment": {
      "id": null,
      "signature": null,
      "status": null
    },
    "metadata": {
      "plan": "pro"
    },
    "failure": {
      "reason": "INSUFFICIENT_FUNDS",
      "attempt_no": 1,
      "next_attempt_at": "2026-10-28T11:00:00.000Z"
    }
  }
}
```

> **Renewals charge the payer's wallet:**
>
> Each renewal pulls your price and, when it is above zero, the service fee from the payer's wallet.
> Fianto does not charge missed periods later.

### `subscription.cancel_scheduled`

A cancellation was scheduled for the period end: you cancelled with at: period_end (cancel_reason MERCHANT_CANCELED), or the payer cancelled from the portal or on Solana (PAYER_CANCELED; a cancel made outside the portal is picked up within about 5 minutes). Also sent when the payer resumes while your own period-end cancel stands, which becomes MERCHANT_CANCELED. Not sent again while a cancel is already scheduled.

```json
{
  "id": "evt_66c9ac2cde6346a19e1d5683c17686bc",
  "type": "subscription.cancel_scheduled",
  "timestamp": "2026-10-03T16:20:11.000Z",
  "data": {
    "id": "fian_sub_1543enRaYWeBHUQnvfZU4M4He6NO",
    "object": "subscription",
    "status": "ACTIVE",
    "order_id": "sub_user_42_pro",
    "price_id": "fian_price_2NCT7hYJqQnFadrhpQLHC50tjj6Z",
    "plan_id": "fian_plan_1vRG2vTS0AW2RvlHnpHoAm3CMuqn",
    "product_name": "Pro plan",
    "amount": "10000000",
    "fee_amount": "100000",
    "total_amount": "10100000",
    "currency": "USDC",
    "interval": "MONTH",
    "period_hours": 720,
    "customer": {
      "id": "fian_cus_0cJCFTfDw8ZkPLbdr7m2d62PU0SL",
      "wallet": "FxLsWPrqTpmc1yHrKoYj8SE75mAzavKwPDen7Y2xF7pw",
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "current_period_index": 0,
    "current_period_start": "2026-09-28T10:00:00.000Z",
    "current_period_end": "2026-10-28T10:00:00.000Z",
    "cancel_at_period_end": true,
    "cancel_at": "2026-10-28T10:00:00.000Z",
    "cancel_reason": "PAYER_CANCELED",
    "merchant_cancel_requested": false,
    "end_reason": null,
    "started_at": "2026-09-28T10:00:00.000Z",
    "ended_at": null,
    "created_at": "2026-09-28T10:00:00.000Z",
    "payment": {
      "id": null,
      "signature": null,
      "status": null
    },
    "metadata": {
      "plan": "pro"
    }
  }
}
```

### `subscription.cancel_withdrawn`

The payer resumed their cancelled subscription from another app that supports it. Fianto sees the resume only when the cancellation would have taken effect, at the period end, and sends this instead of subscription.ended: the scheduled cancel is cleared and renewals resume. If you had also cancelled at period end, you get subscription.cancel_scheduled instead.

```json
{
  "id": "evt_1722c3105cbff3124326ad46b28deb67",
  "type": "subscription.cancel_withdrawn",
  "timestamp": "2026-10-28T10:00:40.000Z",
  "data": {
    "id": "fian_sub_1543enRaYWeBHUQnvfZU4M4He6NO",
    "object": "subscription",
    "status": "ACTIVE",
    "order_id": "sub_user_42_pro",
    "price_id": "fian_price_2NCT7hYJqQnFadrhpQLHC50tjj6Z",
    "plan_id": "fian_plan_1vRG2vTS0AW2RvlHnpHoAm3CMuqn",
    "product_name": "Pro plan",
    "amount": "10000000",
    "fee_amount": "100000",
    "total_amount": "10100000",
    "currency": "USDC",
    "interval": "MONTH",
    "period_hours": 720,
    "customer": {
      "id": "fian_cus_0cJCFTfDw8ZkPLbdr7m2d62PU0SL",
      "wallet": "FxLsWPrqTpmc1yHrKoYj8SE75mAzavKwPDen7Y2xF7pw",
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "current_period_index": 0,
    "current_period_start": "2026-09-28T10:00:00.000Z",
    "current_period_end": "2026-10-28T10:00:00.000Z",
    "cancel_at_period_end": false,
    "cancel_at": null,
    "cancel_reason": null,
    "merchant_cancel_requested": false,
    "end_reason": null,
    "started_at": "2026-09-28T10:00:00.000Z",
    "ended_at": null,
    "created_at": "2026-09-28T10:00:00.000Z",
    "payment": {
      "id": null,
      "signature": null,
      "status": null
    },
    "metadata": {
      "plan": "pro"
    }
  }
}
```

### `subscription.ended`

The subscription is ENDED. end_reason says why: MERCHANT_CANCELED (you cancelled with at: now, or your period-end cancel took effect), PAYER_CANCELED (the payer's cancel took effect, or the payer's new subscription from the same wallet replaced this one) or PAYMENT_FAILED (the 4th counted failure, sent with failure). A scheduled cancel waits for a renewal charge that is in flight.

```json
{
  "id": "evt_d7049bfb68672e6f9949344f1230dff7",
  "type": "subscription.ended",
  "timestamp": "2026-10-12T09:15:00.000Z",
  "data": {
    "id": "fian_sub_1543enRaYWeBHUQnvfZU4M4He6NO",
    "object": "subscription",
    "status": "ENDED",
    "order_id": "sub_user_42_pro",
    "price_id": "fian_price_2NCT7hYJqQnFadrhpQLHC50tjj6Z",
    "plan_id": "fian_plan_1vRG2vTS0AW2RvlHnpHoAm3CMuqn",
    "product_name": "Pro plan",
    "amount": "10000000",
    "fee_amount": "100000",
    "total_amount": "10100000",
    "currency": "USDC",
    "interval": "MONTH",
    "period_hours": 720,
    "customer": {
      "id": "fian_cus_0cJCFTfDw8ZkPLbdr7m2d62PU0SL",
      "wallet": "FxLsWPrqTpmc1yHrKoYj8SE75mAzavKwPDen7Y2xF7pw",
      "email": "ada@example.com",
      "reference": "user_42"
    },
    "current_period_index": 0,
    "current_period_start": "2026-09-28T10:00:00.000Z",
    "current_period_end": "2026-10-28T10:00:00.000Z",
    "cancel_at_period_end": false,
    "cancel_at": null,
    "cancel_reason": null,
    "merchant_cancel_requested": false,
    "end_reason": "MERCHANT_CANCELED",
    "started_at": "2026-09-28T10:00:00.000Z",
    "ended_at": "2026-10-12T09:15:00.000Z",
    "created_at": "2026-09-28T10:00:00.000Z",
    "payment": {
      "id": null,
      "signature": null,
      "status": null
    },
    "metadata": {
      "plan": "pro"
    }
  }
}
```

> **A cancel from you changes nothing on Solana:**
>
> When you cancel, the payer's subscription stays live on Solana, and a charge already in flight can
> still land after `subscription.ended`. Cancelling refunds nothing and cannot be undone.

## Testing and verification [#testing-and-verification]

### `test.event`

You asked for a test delivery: POST /v1/webhook/test-event, npx @fianto/cli trigger test.event, or the dashboard's Send test event button (10 per hour per application, shared). It needs a verified URL; otherwise the request fails with 409 webhook_endpoint_not_active and no event is kept. It carries no business data.

```json
{
  "id": "evt_95fc71f3d3e580b81e4eda0132024f74",
  "type": "test.event",
  "timestamp": "2026-09-28T09:58:00.000Z",
  "data": {
    "message": "This is a test event from Fianto."
  }
}
```

### `endpoint.verification`

You set a webhook URL or clicked Verify again. Signed like every delivery, but the body has no id. Answer within 5 seconds with HTTP 200 and {"challenge": "<the value you received>"}. The SDK handlers answer it for you.

```json
{
  "type": "endpoint.verification",
  "timestamp": "2026-09-28T09:55:00.000Z",
  "data": {
    "challenge": "MDmHk3jIHMLD8hMUILAlz9cKk5ZkF5BKKsek3kOD6j4"
  }
}
```

## See also [#see-also]

- [Verify webhooks](/developers/webhooks/verify): Handle these events in Next.js, Express or Hono.

- [Webhooks](/developers/webhooks/overview): The envelope, headers and delivery guarantees.

- [Subscriptions](/developers/subscriptions): Renewals, retries and cancelling from your server.

- [Checkout sessions](/developers/checkout-sessions): Create, reissue and cancel one-time sessions.