> For the complete documentation index, see [llms.txt](https://docs.sms4sats.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.sms4sats.com/api-reference/l402.md).

# L402 API

Integrate sms4sats with L402 payment challenges, macaroons, and Lightning preimages.

L402 combines a Lightning invoice with a resource-scoped macaroon. It is designed for AI agents and services that can pay invoices and retrieve payment preimages programmatically.

## Discovery

```bash
curl -sS https://api.sms4sats.com/v2/l402
curl -sS https://api.sms4sats.com/v2/l402/services
curl -sS https://api.sms4sats.com/v2/l402/catalog
```

| Endpoint                | Response                                 |
| ----------------------- | ---------------------------------------- |
| `GET /v2/l402/services` | `{ "services": [{ "code", "name" }] }`   |
| `GET /v2/l402/catalog`  | `{ "items": [...], "updatedAt": "..." }` |

The catalog lists receive-SMS and reactivation offers and may be empty. Listed availability is not guaranteed until the order is allocated after payment; a paid receive-SMS allocation failure follows the refund flow.

## Create an order

<mark style="color:green;">`POST`</mark> `/v2/l402/order`

### Order types

{% tabs %}
{% tab title="Receive SMS" %}

```json
{
  "type": "receive-sms",
  "country": "auto",
  "service": "tg",
  "refundInvoice": "lnbc1..."
}
```

`refundInvoice` is required. It must be a valid **no-amount** BOLT11 invoice with at least 30 minutes of validity remaining when the order is created. That is only the validation floor: choose an invoice whose expiry covers payment delay, the full fulfillment timeout, and routing headroom. If it expires before a refund is attempted, the order can end in `refund_failed`.
{% endtab %}

{% tab title="Reactivate" %}

```json
{
  "type": "receive-sms-reactivate",
  "country": "US",
  "service": "tg",
  "refundInvoice": "lnbc1...",
  "reactivationOfActivationId": "123456789"
}
```

`reactivationOfActivationId` is required and may be a string or number. Reactivation uses the same required refund-invoice flow as a one-time receive-SMS order. Availability depends on the original activation ID.
{% endtab %}

{% tab title="Rent Number" %}

```json
{
  "type": "rent-number",
  "country": "US",
  "service": "tg",
  "durationMinutes": 120
}
```

Do not send `refundInvoice` for a rental. Once its L402 invoice is paid, the rental has no refund flow, including if allocation fails.
{% endtab %}
{% endtabs %}

`country` and `service` are required strings of 1–64 characters. Country accepts `auto`, supported numeric country ID, ISO alpha-2 code, or recognized English name. Service accepts a code from `/v2/l402/services` or a recognized name; `other` maps to `ot`. Rental `durationMinutes`, when supplied, must be a positive integer. It defaults to 120 minutes; pricing and allocation use a two-hour minimum and round upward to a whole hour.

{% hint style="warning" %}
A paid L402 rental has no refund path, including if provider allocation fails. Use current discovery data and accept this risk before paying a rental invoice.
{% endhint %}

### Payment challenge

Creation returns HTTP `402 Payment Required` and a `WWW-Authenticate` header.

```json
{
  "error": "PAYMENT_REQUIRED",
  "orderId": "<payment-hash>",
  "priceSats": 350,
  "invoice": "lnbc...",
  "macaroon": "<serialized-macaroon>",
  "orderStatus": "pending_payment"
}
```

```http
WWW-Authenticate: L402 macaroon="...", invoice="lnbc..."
```

Pay `invoice`, retrieve its 32-byte preimage, and retain all three credentials: `orderId`, `macaroon`, and the preimage. The macaroon carries an `expires_at` caveat (currently 3,600 seconds after creation) and has no refresh route. Polling and cancellation fail with `EXPIRED_MACAROON` after that deadline.

**Rental access limitation:** L402 credentials expire after one hour, before the minimum two-hour rental finishes, and cannot be refreshed. You cannot continue retrieving messages through this API after credential expiry. Prefer the bearer-token rental API for access throughout a rental.

{% hint style="warning" %}
L402 creation does not accept an idempotency key. Do not blindly repeat `POST /v2/l402/order` after an ambiguous network response; each successful creation can issue a separate invoice. Preserve the first successful challenge response and poll its `orderId`.
{% endhint %}

## Authenticate and poll

<mark style="color:blue;">`GET`</mark> `/v2/l402/order/{orderId}`

```bash
curl -sS https://api.sms4sats.com/v2/l402/order/ORDER_ID \
  -H 'Authorization: L402 <serialized-macaroon>:<preimage-hex>'
```

The first valid authenticated request proves payment, marks the order paid, and starts allocation. Poll every 5–10 seconds. The response includes order state, assigned `phoneNumber`, timestamps, and `messages` when available.

```json
{
  "id": "<payment-hash>",
  "type": "receive-sms",
  "country": "85",
  "service": "tg",
  "status": "waiting_for_sms",
  "orderStatus": "waiting_for_sms",
  "fulfillmentStatus": "waiting_for_sms",
  "amountSats": 350,
  "refundAmountSats": null,
  "phoneNumber": "+1...",
  "messages": [],
  "createdAt": "2026-09-30T19:00:00.000Z",
  "updatedAt": "2026-09-30T19:01:10.000Z",
  "paidAt": "2026-09-30T19:01:00.000Z",
  "expiresAt": "2026-09-30T19:21:00.000Z",
  "completedAt": null,
  "refundedAt": null,
  "failedAt": null
}
```

{% hint style="danger" %}
The macaroon and preimage together authorize the order. Treat the complete `Authorization` value as a secret and never log or expose it.
{% endhint %}

## Cancel and refund

<mark style="color:green;">`POST`</mark> `/v2/l402/order/{orderId}/cancel`

Use the same L402 authorization header. Cancellation is available for paid `receive-sms` and `receive-sms-reactivate` orders at least three minutes after payment and before any SMS arrives. Rentals cannot use this cancellation flow. Eligible cancellations refund to the supplied no-amount `refundInvoice`.

Refunds can be slightly lower than `priceSats` because of routing fees.

Orders without an SMS follow the refund flow when they time out. Do not assume a timeout or network error is final—poll the existing order.
