# 1001SMS Reseller API — full reference

> 1001SMS is a reseller API for three products on one balance: SMS activations (a number for one verification code), virtual numbers (a number rented for 12 hours to a year, with an inbox), and eSIM data plans (activation code and QR delivered by API). Every endpoint takes an API key as `Authorization: Bearer <key>`, answers `{ "success": true, "data": ... }` or `{ "success": false, "error": "..." }`, and each key can have a signed webhook so events are pushed instead of polled.

Base URL: `https://www.1001sms.com/api/v1`. API version 2.0.0. Human docs: https://www.1001sms.com/api-docs. OpenAPI JSON: https://www.1001sms.com/api/v1/docs. Short index: https://www.1001sms.com/api-docs/llms.txt.

## Authentication and scopes

Every endpoint requires an API key. Send `Authorization: Bearer YOUR_API_KEY` (recommended) or `?apiKey=YOUR_API_KEY`. Keys are created in the account (user menu → API), up to ten per account, each with its own scopes and webhook. Rate limit: 60 requests per minute per key.

```bash
curl "https://www.1001sms.com/api/v1/informative/countries" -H "Authorization: Bearer YOUR_API_KEY"
```

| Scope | Grants |
|---|---|
| `read` | Lookup and account endpoints: countries, services, prices, balance, transactions. |
| `activate` | Every /activations/* endpoint. |
| `numbers` | Every /numbers/* endpoint. |
| `esim` | Every /esims/* endpoint. |

Webhook endpoints need no scope: they act on the calling key itself.

## Response envelope and errors

```json
{ "success": true, "data": { } }
{ "success": false, "error": "Error message" }
{ "success": false, "error": "Insufficient balance", "required": 12.5 }
```

| HTTP | Meaning |
|---|---|
| `200` | OK |
| `201` | Created (an order was placed) |
| `400` | Invalid parameter or body |
| `401` | Missing, invalid or expired API key |
| `402` | Insufficient balance; `required` says how much the call needed |
| `403` | The key lacks the scope, or the object belongs to someone else |
| `404` | Not found |
| `429` | Rate limit exceeded (per key, per minute) |
| `500` | Internal error |
| `502` | The provider failed; any charge was refunded |
| `503` | Product temporarily unavailable |

## The three products

| | Activation | Virtual number | eSIM |
|---|---|---|---|
| What it is | A number for one verification code | A number that is yours for 12 h to 365 d | A travel data plan for a handset |
| Lives | Minutes | The term you rent, renewable | The plan term, from first activation |
| You get | Phone number, then the SMS | Phone number and an inbox | LPA activation code, QR, ICCID, usage |
| Push events | `sms.received` | `sms.received`, `number.*` | `esim.*` |
| Scope | `activate` | `numbers` | `esim` |

## Lookup

What can be bought as an activation: the services (apps) we verify, the countries, and the live price per operator. Prices are what you pay; a provider is only ever named by its alias EXTRA, MAIN, EXTRA 2 or NEW.

### Three ways to ask for prices

1. **By country.** Only country: every service and operator sold there. `GET https://www.1001sms.com/api/v1/informative/pricing?country=united-states`
2. **By service.** Only service: every country and operator that sells it. `GET https://www.1001sms.com/api/v1/informative/pricing?service=google`
3. **By both.** The operator list for one market pair. This is what you order with. `GET https://www.1001sms.com/api/v1/informative/pricing?country=US&service=google`

### GET /informative/services

Available services. Active services (apps/platforms) available for verification. Use the slug as the service value in pricing and order calls.

- Scope: `read`
- Docs: https://www.1001sms.com/api-docs#services

**Example**

```bash
curl "https://www.1001sms.com/api/v1/informative/services" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": [
    {
      "id": "clx_service",
      "name": "Google",
      "slug": "google",
      "price": "0.50",
      "popular": true,
      "iconPath": "https://www.1001sms.com/icons/google.svg"
    }
  ]
}
```

### GET /informative/countries

Available countries. Active countries where activation numbers are sold. Use the code or slug in pricing and order calls.

- Scope: `read`
- Docs: https://www.1001sms.com/api-docs#countries

**Example**

```bash
curl "https://www.1001sms.com/api/v1/informative/countries" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": [
    {
      "id": "clx_country",
      "name": "United States",
      "code": "US",
      "slug": "united-states",
      "flag": "US",
      "continent": "NORTH_AMERICA",
      "phoneCode": "+1"
    }
  ]
}
```

### GET /informative/pricing

Prices by country. Pass country only to get every service and operator available in that country.

- Scope: `read`
- Docs: https://www.1001sms.com/api-docs#pricing-country

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `country` | string | yes | Country code, name or slug. Example: US, Canada, canada. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/informative/pricing?country=canada" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "country": "Canada",
    "countryCode": "CA",
    "countrySlug": "canada",
    "services": [
      {
        "service": "google",
        "serviceName": "Google",
        "operators": [
          {
            "operator": "Foxtrot",
            "price": 0.49,
            "count": 13607,
            "deliverability": 73,
            "provider": "MAIN"
          }
        ]
      }
    ]
  }
}
```

### GET /informative/pricing

Prices by service. Pass service only to get every country and operator for that service.

- Scope: `read`
- Docs: https://www.1001sms.com/api-docs#pricing-service

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `service` | string | yes | Service slug from /informative/services. Example: google. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/informative/pricing?service=google" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "service": "google",
    "countries": [
      {
        "country": "United States",
        "countryCode": "US",
        "countrySlug": "united-states",
        "operators": [
          {
            "operator": "virtual9",
            "price": 0.45,
            "count": 1200,
            "deliverability": 92,
            "provider": "EXTRA"
          }
        ]
      }
    ]
  }
}
```

### GET /informative/pricing

Prices by service and country. Pass both to get the exact operator list for one market pair.

- Scope: `read`
- Docs: https://www.1001sms.com/api-docs#pricing-service-country

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `country` | string | yes | Country code, name or slug. |
| `service` | string | yes | Service slug. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/informative/pricing?country=US&service=google" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "country": "US",
    "service": "google",
    "operators": [
      {
        "operator": "Foxtrot",
        "price": 0.49,
        "count": 13607,
        "deliverability": 73,
        "provider": "MAIN"
      }
    ]
  }
}
```

## Activations

A short-lived number for one verification code. You pick the country, the service and a provider alias, get a number, and either poll for the SMS or let the sms.received webhook push it to you. No SMS within the window means an automatic refund.

### How an activation works

1. **Price it.** Ask for the operators of the market pair and pick one (or let us pick). `GET https://www.1001sms.com/api/v1/informative/pricing?country=US&service=google`
2. **Order.** The balance is charged at once; the response carries the number. `POST https://www.1001sms.com/api/v1/activations/order`
3. **Wait for the code.** Poll /activations/check every few seconds, or subscribe to sms.received and do nothing. `POST https://www.1001sms.com/api/v1/activations/check`
4. **Done, or cancel.** Received: you are done. Nothing arrived: cancel for a full refund, or wait for the automatic one. `POST https://www.1001sms.com/api/v1/activations/cancel`

### POST /activations/order

Order an activation number. Places a new activation order and returns the rented number. The balance is charged immediately and refunded automatically when the provider fails.

- Scope: `activate`
- Docs: https://www.1001sms.com/api-docs#order

**Request body (JSON)**

| Name | Type | Required | Description |
|---|---|---|---|
| `country` | string | yes | Country code, name or slug. |
| `service` | string | yes | Service slug. |
| `provider` | string | yes | EXTRA, MAIN, EXTRA 2 or NEW. |
| `purchaseType` | string | yes | Must be exactly "activation". |
| `operator` | string | no | Preferred operator/pool. If omitted, the best option is selected. |

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/activations/order" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"country":"US","service":"google","provider":"EXTRA","purchaseType":"activation"}'
```

**Response** (201)

```json
{
  "success": true,
  "data": {
    "id": "ord_123",
    "phoneNumber": "+14155552671",
    "countryCode": "US",
    "countryName": "United States",
    "serviceId": "google",
    "serviceName": "Google",
    "operatorName": "virtual9",
    "price": 0.5,
    "status": "RENTED",
    "provider": "EXTRA",
    "providerId": "987654321",
    "expiresAt": "2026-02-01T12:15:00.000Z",
    "rentedAt": "2026-02-01T12:00:00.000Z"
  }
}
```

### POST /activations/check

Check for SMS. Asks the provider for new SMS, stores them and returns the current state. When the order timed out with no SMS it is refunded here.

- Scope: `activate`
- Docs: https://www.1001sms.com/api-docs#check

**Request body (JSON)**

| Name | Type | Required | Description |
|---|---|---|---|
| `orderId` | string | yes | Order id from /activations/order. |

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/activations/check" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"orderId":"VALUE"}'
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "hasMessage": true,
    "messages": [
      {
        "id": "msg_1",
        "sender": "Google",
        "message": "Your code is 482910",
        "code": "482910",
        "receivedAt": "2026-02-01T12:03:00.000Z"
      }
    ],
    "status": "RENTED",
    "providerStatus": "RECEIVED"
  }
}
```

### GET /activations/{orderId}

Get one order. Full order details with every stored message.

- Scope: `activate`
- Docs: https://www.1001sms.com/api-docs#get-order
- Note: Provider is returned as the reseller-safe alias: EXTRA, MAIN, EXTRA 2 or NEW.

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `orderId` | string | yes | The order id. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/activations/ord_123" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "id": "ord_123",
    "phoneNumber": "+14155552671",
    "countryCode": "US",
    "countryName": "United States",
    "serviceId": "google",
    "serviceName": "Google",
    "operatorName": "virtual9",
    "price": 0.5,
    "status": "RENTED",
    "provider": "EXTRA",
    "providerId": "987654321",
    "expiresAt": "2026-02-01T12:15:00.000Z",
    "rentedAt": "2026-02-01T12:00:00.000Z",
    "purchaseType": "activation",
    "messages": []
  }
}
```

### GET /activations/active

Active orders. Paginated orders with status RENTED, ACTIVE or PENDING, each with its latest message.

- Scope: `activate`
- Docs: https://www.1001sms.com/api-docs#active-orders

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `page` | integer | no | Page number, default 1. |
| `limit` | integer | no | Per page, default 20, max 100. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/activations/active" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "orders": [
      {
        "id": "ord_123",
        "phoneNumber": "+14155552671",
        "countryCode": "US",
        "countryName": "United States",
        "serviceId": "google",
        "serviceName": "Google",
        "operatorName": "virtual9",
        "price": 0.5,
        "status": "RENTED",
        "provider": "EXTRA",
        "providerId": "987654321",
        "expiresAt": "2026-02-01T12:15:00.000Z",
        "rentedAt": "2026-02-01T12:00:00.000Z",
        "latestMessage": null
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 20
  }
}
```

### GET /activations/history

Order history. Paginated order history with optional status and date filters.

- Scope: `activate`
- Docs: https://www.1001sms.com/api-docs#history

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `page` | integer | no | Page number, default 1. |
| `limit` | integer | no | Per page, default 20, max 100. |
| `status` | string | no | RENTED, COMPLETED, PENDING, EXPIRED, BLOCKED, REFUNDED. |
| `from` | ISO date | no | Orders rented from this date. |
| `to` | ISO date | no | Orders rented up to this date. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/activations/history?status=EXPIRED&page=1" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "orders": [
      {
        "id": "ord_122",
        "phoneNumber": "+14155552671",
        "countryCode": "US",
        "countryName": "United States",
        "serviceId": "google",
        "serviceName": "Google",
        "operatorName": "virtual9",
        "price": 0.5,
        "status": "EXPIRED",
        "provider": "EXTRA",
        "providerId": "987654321",
        "expiresAt": "2026-02-01T12:15:00.000Z",
        "rentedAt": "2026-02-01T12:00:00.000Z",
        "messageCount": 0
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 20
  }
}
```

### POST /activations/cancel

Cancel an order. Cancels one order. A full refund is given only when no real SMS was received.

- Scope: `activate`
- Docs: https://www.1001sms.com/api-docs#cancel
- Note: Some providers reject very early cancel attempts for about 1–2 minutes. An order past its expiry is settled instead: refunded if no SMS arrived (status EXPIRED). Expired orders are also refunded automatically about 10 minutes after expiry, without a cancel.

**Request body (JSON)**

| Name | Type | Required | Description |
|---|---|---|---|
| `orderId` | string | yes | Order id to cancel. |

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/activations/cancel" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"orderId":"VALUE"}'
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "id": "ord_123",
    "status": "REFUNDED",
    "refundAmount": 0.5,
    "refundReason": "Full refund - No SMS received"
  }
}
```

### POST /activations/cancel-all

Cancel every active order. Attempts to cancel every order with status RENTED, ACTIVE or PENDING and reports each outcome.

- Scope: `activate`
- Docs: https://www.1001sms.com/api-docs#cancel-all

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/activations/cancel-all" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "cancelled": 2,
    "failed": 1,
    "results": [
      {
        "orderId": "ord_1",
        "status": "REFUNDED",
        "refundAmount": 0.5
      },
      {
        "orderId": "ord_2",
        "status": "failed",
        "refundAmount": 0,
        "error": "Cancel failed. Please try again later."
      }
    ]
  }
}
```

### GET /activations/archive-all

Finished orders. Lists terminal orders: EXPIRED, REFUNDED and BLOCKED.

- Scope: `activate`
- Docs: https://www.1001sms.com/api-docs#archive

**Example**

```bash
curl "https://www.1001sms.com/api/v1/activations/archive-all" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "archived": 12,
    "orders": [
      {
        "id": "ord_done_1",
        "status": "EXPIRED",
        "serviceId": "google",
        "country": "US",
        "price": 0.5,
        "rentedAt": "2026-02-01T08:00:00.000Z"
      }
    ]
  }
}
```

## Virtual Numbers

A real number that stays yours for a term of 12 hours to a year and receives SMS from every service ("unlimited"), or a cheaper one dedicated to a single service ("service"). Countries are ISO codes, terms are short codes: 12h, 1d, 7d, 30d, 180d, 365d. Dedicated numbers are sold for 7d, 30d, 180d and 365d.

### How a virtual number works

1. **Pick a country.** Eighteen countries; each sells both number types. `GET https://www.1001sms.com/api/v1/numbers/countries`
2. **See what it costs.** One call returns every unlimited term and every dedicated-service offer for the country. `GET https://www.1001sms.com/api/v1/numbers/pricing?country=GB`
3. **Order.** The balance is charged at the live price. The number is live at once. `POST https://www.1001sms.com/api/v1/numbers/order`
4. **Read the inbox.** Poll the messages endpoint, or subscribe to sms.received and be told. `GET https://www.1001sms.com/api/v1/numbers/{id}/messages`
5. **Keep it.** Renew for another term before it expires, or switch autoRenew on and we do it from your balance. `POST https://www.1001sms.com/api/v1/numbers/{id}/renew`

### GET /numbers/countries

Countries with virtual numbers. The countries where virtual numbers are sold, with the dial code and the number types available.

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-countries

**Example**

```bash
curl "https://www.1001sms.com/api/v1/numbers/countries" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": [
    {
      "code": "GB",
      "name": "United Kingdom",
      "phoneCode": "+44",
      "types": [
        "unlimited",
        "service"
      ]
    }
  ]
}
```

### GET /numbers/pricing

Prices for one country. Everything sold in a country at today's price: the unlimited terms and, per dedicated service, its terms. `service` values are what you pass to /numbers/order.

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-pricing

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `country` | string | yes | ISO country code from /numbers/countries, e.g. GB. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/numbers/pricing?country=GB" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "country": "GB",
    "countryName": "United Kingdom",
    "unlimited": [
      {
        "term": "1d",
        "days": 1,
        "hours": 24,
        "price": 3.2
      },
      {
        "term": "30d",
        "days": 30,
        "hours": 720,
        "price": 12.5
      }
    ],
    "services": [
      {
        "service": "wa",
        "name": "WhatsApp",
        "fromPrice": 4.1,
        "terms": [
          {
            "term": "30d",
            "days": 30,
            "hours": 720,
            "price": 4.1
          }
        ]
      }
    ]
  }
}
```

### GET /numbers/availability

Stock for a term. How many unlimited numbers are in stock for one country and term right now.

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-availability

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `country` | string | yes | ISO country code. |
| `term` | string | yes | 12h, 1d, 7d, 30d, 180d or 365d. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/numbers/availability?country=GB&term=1d" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "country": "GB",
    "term": "1d",
    "days": 1,
    "hours": 24,
    "count": 42,
    "inStock": true
  }
}
```

### POST /numbers/order

Rent a virtual number. Rents a number from the balance at the live price and returns it. Unlimited numbers may carry a nickname and auto-renew; dedicated ones need a service.

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-order
- Note: The price is whatever /numbers/pricing shows at the moment of the order. 402 means the balance is short; the response carries `required`.

**Request body (JSON)**

| Name | Type | Required | Description |
|---|---|---|---|
| `country` | string | yes | ISO country code. |
| `term` | string | yes | 12h, 1d, 7d, 30d, 180d or 365d. |
| `type` | string | no | "unlimited" (default) or "service". |
| `service` | string | no | Required for type "service": a service id from /numbers/pricing. |
| `autoRenew` | boolean | no | Unlimited only. Renew from the balance before the term ends. |
| `nickname` | string | no | Unlimited only. A label of letters, numbers and spaces. |

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/numbers/order" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"country":"GB","term":"30d","type":"unlimited","autoRenew":false}'
```

**Response** (201)

```json
{
  "success": true,
  "data": {
    "id": "num_8f2k",
    "phoneNumber": "+447700900123",
    "country": "GB",
    "countryName": "United Kingdom",
    "type": "unlimited",
    "service": null,
    "serviceName": null,
    "term": "30d",
    "days": 30,
    "status": "ACTIVE",
    "price": 12.5,
    "autoRenew": false,
    "nickname": null,
    "rentedAt": "2026-09-22T10:00:00.000Z",
    "expiresAt": "2026-10-22T10:00:00.000Z",
    "messages": []
  }
}
```

### GET /numbers

Your virtual numbers. Your virtual numbers, newest first, with a message count each.

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-list

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `status` | string | no | active, expired or refunded. |
| `page` | integer | no | Page number, default 1. |
| `limit` | integer | no | Per page, default 20, max 100. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/numbers" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "numbers": [
      {
        "id": "num_8f2k",
        "phoneNumber": "+447700900123",
        "country": "GB",
        "countryName": "United Kingdom",
        "type": "unlimited",
        "service": null,
        "serviceName": null,
        "term": "30d",
        "days": 30,
        "status": "ACTIVE",
        "price": 12.5,
        "autoRenew": false,
        "nickname": null,
        "rentedAt": "2026-09-22T10:00:00.000Z",
        "expiresAt": "2026-10-22T10:00:00.000Z",
        "messageCount": 3
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 20
  }
}
```

### GET /numbers/{id}

One virtual number. One number with every stored message, newest first. `status` is ACTIVE, EXPIRED, REFUNDED or PENDING.

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-get

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | The number id. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/numbers/ID_ID" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "id": "num_8f2k",
    "phoneNumber": "+447700900123",
    "country": "GB",
    "countryName": "United Kingdom",
    "type": "unlimited",
    "service": null,
    "serviceName": null,
    "term": "30d",
    "days": 30,
    "status": "ACTIVE",
    "price": 12.5,
    "autoRenew": false,
    "nickname": null,
    "rentedAt": "2026-09-22T10:00:00.000Z",
    "expiresAt": "2026-10-22T10:00:00.000Z",
    "messages": [
      {
        "id": "msg_1",
        "sender": "WhatsApp",
        "text": "Your WhatsApp code: 482-910",
        "code": "482910",
        "receivedAt": "2026-09-22T10:03:00.000Z"
      }
    ]
  }
}
```

### GET /numbers/{id}/messages

The inbox. Asks the provider for anything new, stores it and returns every message, newest first. `fetched` is false when the provider did not answer; you then get what was already stored.

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-messages
- Note: Polling every 10 seconds is fine. Subscribing to the sms.received webhook is better.

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | The number id. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/numbers/ID_ID/messages" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "numberId": "num_8f2k",
    "fetched": true,
    "newMessages": 1,
    "messages": [
      {
        "id": "msg_1",
        "sender": "WhatsApp",
        "text": "Your WhatsApp code: 482-910",
        "code": "482910",
        "receivedAt": "2026-09-22T10:03:00.000Z"
      }
    ]
  }
}
```

### GET /numbers/{id}/renew

Renewal options. The terms this number can be renewed for, at today's price. A dedicated number renews at its service's own tariff.

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-renew-options

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | The number id. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/numbers/ID_ID/renew" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "numberId": "num_8f2k",
    "expiresAt": "2026-10-22T10:00:00.000Z",
    "options": [
      {
        "term": "7d",
        "days": 7,
        "hours": 168,
        "price": 6.4
      },
      {
        "term": "30d",
        "days": 30,
        "hours": 720,
        "price": 12.5
      }
    ]
  }
}
```

### POST /numbers/{id}/renew

Renew a number. Charges the balance and extends the number by one term from its current expiry. `autoRenew` may be changed at the same time (unlimited numbers).

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-renew

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | The number id. |

**Request body (JSON)**

| Name | Type | Required | Description |
|---|---|---|---|
| `term` | string | yes | One of the options from GET /numbers/{id}/renew. |
| `autoRenew` | boolean | no | Unlimited only. |

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/numbers/num_8f2k/renew" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"term":"30d"}'
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "number": {
      "id": "num_8f2k",
      "phoneNumber": "+447700900123",
      "country": "GB",
      "countryName": "United Kingdom",
      "type": "unlimited",
      "service": null,
      "serviceName": null,
      "term": "30d",
      "days": 30,
      "status": "ACTIVE",
      "price": 12.5,
      "autoRenew": false,
      "nickname": null,
      "rentedAt": "2026-09-22T10:00:00.000Z",
      "expiresAt": "2026-11-21T10:00:00.000Z"
    },
    "renewal": {
      "term": "30d",
      "days": 30,
      "hours": 720,
      "price": 12.5,
      "expiresAt": "2026-11-21T10:00:00.000Z"
    }
  }
}
```

### POST /numbers/{id}/cancel

Cancel a number. Hands an unlimited number back and refunds it. Only within 120 minutes of purchase and only if no SMS arrived. Dedicated numbers cannot be cancelled.

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-cancel

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | The number id. |

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/numbers/ID_ID/cancel" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "number": {
      "id": "num_8f2k",
      "phoneNumber": "+447700900123",
      "country": "GB",
      "countryName": "United Kingdom",
      "type": "unlimited",
      "service": null,
      "serviceName": null,
      "term": "30d",
      "days": 30,
      "status": "REFUNDED",
      "price": 12.5,
      "autoRenew": false,
      "nickname": null,
      "rentedAt": "2026-09-22T10:00:00.000Z",
      "expiresAt": "2026-10-22T10:00:00.000Z"
    },
    "refundAmount": 12.5
  }
}
```

### PATCH /numbers/{id}

Change auto-renew or nickname. Unlimited numbers only. Switch auto-renew on or off, or set a nickname (empty string clears it).

- Scope: `numbers`
- Docs: https://www.1001sms.com/api-docs#numbers-patch

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | The number id. |

**Request body (JSON)**

| Name | Type | Required | Description |
|---|---|---|---|
| `autoRenew` | boolean | no | Renew from the balance before the term ends. |
| `nickname` | string | no | Letters, numbers and spaces, up to 100 characters. |

**Example**

```bash
curl -X PATCH "https://www.1001sms.com/api/v1/numbers/num_8f2k" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"autoRenew":true,"nickname":"Support line"}'
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "id": "num_8f2k",
    "phoneNumber": "+447700900123",
    "country": "GB",
    "countryName": "United Kingdom",
    "type": "unlimited",
    "service": null,
    "serviceName": null,
    "term": "30d",
    "days": 30,
    "status": "ACTIVE",
    "price": 12.5,
    "autoRenew": true,
    "nickname": "Support line",
    "rentedAt": "2026-09-22T10:00:00.000Z",
    "expiresAt": "2026-10-22T10:00:00.000Z"
  }
}
```

## eSIMs

Travel data plans for 200+ destinations. A plan is either a fixed bundle (one price, fixed term) or a day pass (a daily rate; you choose the number of days). Orders are paid from the balance; the activation code and QR usually arrive within seconds, and the esim.allocated webhook tells you when they do.

### How an eSIM order works

1. **Choose a destination.** Countries, regions and global plans, each with a "from" price. `GET https://www.1001sms.com/api/v1/esims/destinations`
2. **Pick a plan.** per_day plans take a `days` value at order time; bundle plans do not. `GET https://www.1001sms.com/api/v1/esims/packages?location=ES`
3. **Quote it.** Longer terms and several eSIMs in one order are discounted; the quote shows the exact total. `POST https://www.1001sms.com/api/v1/esims/quote`
4. **Order.** The response usually already carries the activation code. If `ready` is false, poll the order or wait for esim.allocated. `POST https://www.1001sms.com/api/v1/esims/order`
5. **Deliver.** Hand your customer the LPA activation code or the QR image URL. Refresh the order for usage. `GET https://www.1001sms.com/api/v1/esims/orders/{id}?refresh=1`

### GET /esims/destinations

Destinations. Every destination with sellable plans: a country (ISO code), a region (comma-separated member codes) or a global plan.

- Scope: `esim`
- Docs: https://www.1001sms.com/api-docs#esims-destinations

**Example**

```bash
curl "https://www.1001sms.com/api/v1/esims/destinations" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "count": 1,
    "destinations": [
      {
        "locationCode": "ES",
        "name": "Spain",
        "kind": "country",
        "packageCount": 14,
        "fromPrice": 1.9,
        "fromUnlimited": true,
        "topSpeed": "5G"
      }
    ]
  }
}
```

### GET /esims/packages

Plans for a destination. The plans sold for one destination at retail. `pricingUnit` is per_day (price is a daily rate, pass `days` when ordering) or bundle (one price for `durationDays`). `unlimited` is true for a plan with no cap and for a day pass that keeps working at `fupPolicy` after its daily allowance of `volumeBytes`, which is how we sell it.

- Scope: `esim`
- Docs: https://www.1001sms.com/api-docs#esims-packages

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `location` | string | yes | A locationCode from /esims/destinations. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/esims/packages?location=ES" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "location": "ES",
    "count": 1,
    "packages": [
      {
        "id": "clx_pkg",
        "packageCode": "ES_1_7",
        "name": "Spain 1GB/Day",
        "locationCode": "ES",
        "locationName": "Spain",
        "volumeLabel": "1 GB/day",
        "volumeBytes": "1073741824",
        "durationDays": 1,
        "speed": "5G",
        "price": 1.9,
        "unlimited": true,
        "pricingUnit": "per_day",
        "fupPolicy": "1 Mbps",
        "fupKbps": 1000,
        "ipExport": "ES"
      }
    ]
  }
}
```

### GET /esims/packages/{packageCode}

One plan. One plan by its code.

- Scope: `esim`
- Docs: https://www.1001sms.com/api-docs#esims-package

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `packageCode` | string | yes | The plan code. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/esims/packages/PACKAGECODE_ID" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "id": "clx_pkg",
    "packageCode": "ES_1_7",
    "name": "Spain 1GB/Day",
    "locationCode": "ES",
    "locationName": "Spain",
    "volumeLabel": "1 GB/day",
    "volumeBytes": "1073741824",
    "durationDays": 1,
    "speed": "5G",
    "price": 1.9,
    "unlimited": true,
    "pricingUnit": "per_day",
    "fupPolicy": "1 Mbps",
    "fupKbps": 1000,
    "ipExport": "ES"
  }
}
```

### POST /esims/quote

Price an order. What an order would cost without placing it. Day passes can get a longer-trip discount, and orders of several eSIMs a multi-eSIM discount off the whole order; the rates are set by us and may change, so read them from this quote rather than hard-coding them.

- Scope: `esim`
- Docs: https://www.1001sms.com/api-docs#esims-quote

**Request body (JSON)**

| Name | Type | Required | Description |
|---|---|---|---|
| `packageCode` | string | yes | The plan code. |
| `quantity` | integer | no | How many eSIMs, 1–10. Default 1. |
| `days` | integer | no | Day passes only: the term in days, 1–365. |

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/esims/quote" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"packageCode":"ES_1_7","quantity":1,"days":7}'
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "packageCode": "ES_1_7",
    "packageName": "Spain Unlimited · 7 days",
    "pricingUnit": "per_day",
    "unlimited": true,
    "quantity": 1,
    "days": 7,
    "unitPrice": 1.9,
    "listPrice": 13.3,
    "termDiscount": 1.33,
    "multiEsimDiscount": 0,
    "price": 11.97
  }
}
```

### POST /esims/order

Buy an eSIM. Buys the plan from the balance and returns the order. `ready: true` means the profiles carry an activation code; otherwise poll GET /esims/orders/{id} or wait for the esim.allocated webhook. A provider failure refunds the balance and returns 502.

- Scope: `esim`
- Docs: https://www.1001sms.com/api-docs#esims-order

**Request body (JSON)**

| Name | Type | Required | Description |
|---|---|---|---|
| `packageCode` | string | yes | The plan code. |
| `quantity` | integer | no | How many eSIMs, 1–10. Default 1. |
| `days` | integer | no | Day passes only: the term in days, 1–365. |

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/esims/order" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"packageCode":"ES_1_7","quantity":1,"days":7}'
```

**Response** (201)

```json
{
  "success": true,
  "data": {
    "id": "eord_9x1",
    "status": "ALLOCATED",
    "packageCode": "ES_1_7",
    "packageName": "Spain Unlimited · 7 days",
    "unlimited": true,
    "days": 7,
    "locationCode": "ES",
    "quantity": 1,
    "price": 12.1,
    "createdAt": "2026-09-22T10:00:00.000Z",
    "ready": true,
    "profiles": [
      {
        "id": "prof_1",
        "esimTranNo": "T2026092200001",
        "iccid": "8943100000000000001",
        "activationCode": "LPA:1$rsp-eu.example.com$ABCDEF123456",
        "qrCodeUrl": "https://cdn.example.com/qr/T2026092200001.png",
        "shortUrl": "https://esim.example.com/s/abc",
        "apn": "internet",
        "status": "GOT_RESOURCE",
        "smdpStatus": "RELEASED",
        "totalVolume": "1073741824",
        "usedVolume": "0",
        "activatedAt": null,
        "expiresAt": null
      }
    ],
    "lifecycle": "ready"
  }
}
```

### GET /esims/orders

Your eSIM orders. Your orders, newest first, each with its profiles. `packageName` is what the order was sold as ("Spain Unlimited · 7 days"); `packageCode` identifies the plan. `days` is the term of a day pass (null for a bundle). For an `unlimited` order a profile's `totalVolume` is the daily full-speed allowance, not a cap: show the order as unlimited and `usedVolume` as data used.

- Scope: `esim`
- Docs: https://www.1001sms.com/api-docs#esims-orders

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `page` | integer | no | Page number, default 1. |
| `limit` | integer | no | Per page, default 20, max 100. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/esims/orders" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "orders": [
      {
        "id": "eord_9x1",
        "status": "ALLOCATED",
        "packageCode": "ES_1_7",
        "packageName": "Spain Unlimited · 7 days",
        "unlimited": true,
        "days": 7,
        "locationCode": "ES",
        "quantity": 1,
        "price": 12.1,
        "createdAt": "2026-09-22T10:00:00.000Z",
        "ready": true,
        "profiles": [
          {
            "id": "prof_1",
            "esimTranNo": "T2026092200001",
            "iccid": "8943100000000000001",
            "activationCode": "LPA:1$rsp-eu.example.com$ABCDEF123456",
            "qrCodeUrl": "https://cdn.example.com/qr/T2026092200001.png",
            "shortUrl": "https://esim.example.com/s/abc",
            "apn": "internet",
            "status": "GOT_RESOURCE",
            "smdpStatus": "RELEASED",
            "totalVolume": "1073741824",
            "usedVolume": "0",
            "activatedAt": null,
            "expiresAt": null
          }
        ],
        "lifecycle": "ready"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 20
  }
}
```

### GET /esims/orders/{id}

One order. One order with its profiles. While nothing is allocated yet the provider is asked again before answering. `refresh=1` also refreshes the data counters and status of each profile. `lifecycle` is preparing, ready, active, expired, failed, cancelled or refunded.

- Scope: `esim`
- Docs: https://www.1001sms.com/api-docs#esims-order-get

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | The order id. |

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `refresh` | string | no | Pass 1 to refresh usage from the provider. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/esims/orders/eord_9x1?refresh=1" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "id": "eord_9x1",
    "status": "ALLOCATED",
    "packageCode": "ES_1_7",
    "packageName": "Spain Unlimited · 7 days",
    "unlimited": true,
    "days": 7,
    "locationCode": "ES",
    "quantity": 1,
    "price": 12.1,
    "createdAt": "2026-09-22T10:00:00.000Z",
    "ready": true,
    "profiles": [
      {
        "id": "prof_1",
        "esimTranNo": "T2026092200001",
        "iccid": "8943100000000000001",
        "activationCode": "LPA:1$rsp-eu.example.com$ABCDEF123456",
        "qrCodeUrl": "https://cdn.example.com/qr/T2026092200001.png",
        "shortUrl": "https://esim.example.com/s/abc",
        "apn": "internet",
        "status": "IN_USE",
        "smdpStatus": "ENABLED",
        "totalVolume": "1073741824",
        "usedVolume": "524288000",
        "activatedAt": "2026-09-23T08:00:00.000Z",
        "expiresAt": "2026-09-30T08:00:00.000Z"
      }
    ],
    "lifecycle": "active"
  }
}
```

## Webhooks

Instead of polling, give each API key a URL and we POST signed JSON to it when something happens: an SMS arrives, a number is renewed or expires, an eSIM is allocated. Configure it in the key manager on the website or with the endpoints below; either way it belongs to the key, so a key with the read scope alone can still receive events.

### How delivery works

1. **Set a URL.** https on a public host. The first save issues a signing secret; keep it. `PUT https://www.1001sms.com/api/v1/webhooks/config`
2. **Verify every request.** Recompute the HMAC over the timestamp and the raw body with your secret before trusting a payload.
3. **Answer 2xx fast.** Within 10 seconds. Do the work afterwards. Anything else is retried 1 m, 5 m, 30 m, 2 h and 12 h later, then marked FAILED.
4. **Dedupe on id.** A retry carries the same event id. Twenty failed deliveries in a row switch the webhook off until the URL is saved again.

### Events and payloads

Every delivery is one JSON object with `id`, `event`, `createdAt` and `data`. The `data` of each event mirrors the matching GET endpoint.

```http
POST /your/endpoint HTTP/1.1
Content-Type: application/json
User-Agent: 1001SMS-Webhooks/1.0
X-1001SMS-Event: sms.received
X-1001SMS-Delivery: dlv_01j8x…
X-1001SMS-Signature: t=1758535381,v1=5f1c…e9a0

{
  "id": "evt_3f9a1c2b7d8e4f50a1b2",
  "event": "sms.received",
  "createdAt": "2026-09-22T10:03:01.000Z",
  "data": {
    "product": "virtual_number",
    "numberId": "num_8f2k",
    "phoneNumber": "+447700900123",
    "country": "GB",
    "service": "full",
    "serviceName": "All services",
    "message": {
      "id": "msg_1",
      "sender": "WhatsApp",
      "text": "Your WhatsApp code: 482-910",
      "code": "482910",
      "receivedAt": "2026-09-22T10:03:00.000Z"
    }
  }
}
```

| Event | Group | When |
|---|---|---|
| `sms.received` | SMS | An SMS arrived on an activation number or a virtual number. Fired the moment the provider tells us, and also when a poll finds a new message. |
| `number.activated` | Virtual numbers | A virtual number was rented (by the API or on the website). |
| `number.renewed` | Virtual numbers | A virtual number was renewed by you, the API or auto-renew. |
| `number.renewal_failed` | Virtual numbers | Auto-renew ran and the balance was short. |
| `number.expiring` | Virtual numbers | A number expires within a day (sent once per term). |
| `number.expired` | Virtual numbers | A number's term ended. |
| `number.cancelled` | Virtual numbers | An unlimited number was cancelled within its window and refunded. |
| `esim.allocated` | eSIMs | An order received its activation code and QR. |
| `esim.updated` | eSIMs | A profile's status or data usage changed (provider event or a refresh). |
| `esim.failed` | eSIMs | The provider rejected the order after the balance was taken; the balance was refunded. There is no order id yet, so the request is echoed. |
| `esim.refunded` | eSIMs | Support refunded the order. |
| `ping` | Test | Sent by the test button and POST /webhooks/test. Always delivered, whatever the subscriptions. |

### Sample `data` for every event

**sms.received**

```json
{
  "product": "virtual_number",
  "numberId": "num_8f2k",
  "phoneNumber": "+447700900123",
  "country": "GB",
  "service": "full",
  "serviceName": "All services",
  "message": {
    "id": "msg_1",
    "sender": "WhatsApp",
    "text": "Your WhatsApp code: 482-910",
    "code": "482910",
    "receivedAt": "2026-09-22T10:03:00.000Z"
  }
}
```

**number.activated**

```json
{
  "number": {
    "id": "num_8f2k",
    "phoneNumber": "+447700900123",
    "country": "GB",
    "countryName": "United Kingdom",
    "type": "unlimited",
    "service": null,
    "serviceName": null,
    "term": "30d",
    "days": 30,
    "status": "ACTIVE",
    "price": 12.5,
    "autoRenew": false,
    "nickname": null,
    "rentedAt": "2026-09-22T10:00:00.000Z",
    "expiresAt": "2026-10-22T10:00:00.000Z",
    "messageCount": 0
  }
}
```

**number.renewed**

```json
{
  "number": {
    "id": "num_8f2k",
    "phoneNumber": "+447700900123",
    "country": "GB",
    "countryName": "United Kingdom",
    "type": "unlimited",
    "service": null,
    "serviceName": null,
    "term": "30d",
    "days": 30,
    "status": "ACTIVE",
    "price": 12.5,
    "autoRenew": false,
    "nickname": null,
    "rentedAt": "2026-09-22T10:00:00.000Z",
    "expiresAt": "2026-11-21T10:00:00.000Z",
    "messageCount": 0
  },
  "price": 12.5,
  "days": 30
}
```

**number.renewal_failed**

```json
{
  "number": {
    "id": "num_8f2k",
    "phoneNumber": "+447700900123",
    "country": "GB",
    "countryName": "United Kingdom",
    "type": "unlimited",
    "service": null,
    "serviceName": null,
    "term": "30d",
    "days": 30,
    "status": "ACTIVE",
    "price": 12.5,
    "autoRenew": false,
    "nickname": null,
    "rentedAt": "2026-09-22T10:00:00.000Z",
    "expiresAt": "2026-10-22T10:00:00.000Z",
    "messageCount": 0
  },
  "price": 12.5,
  "balance": 3.1
}
```

**number.expiring**

```json
{
  "number": {
    "id": "num_8f2k",
    "phoneNumber": "+447700900123",
    "country": "GB",
    "countryName": "United Kingdom",
    "type": "unlimited",
    "service": null,
    "serviceName": null,
    "term": "30d",
    "days": 30,
    "status": "ACTIVE",
    "price": 12.5,
    "autoRenew": false,
    "nickname": null,
    "rentedAt": "2026-09-22T10:00:00.000Z",
    "expiresAt": "2026-10-22T10:00:00.000Z",
    "messageCount": 0
  }
}
```

**number.expired**

```json
{
  "number": {
    "id": "num_8f2k",
    "phoneNumber": "+447700900123",
    "country": "GB",
    "countryName": "United Kingdom",
    "type": "unlimited",
    "service": null,
    "serviceName": null,
    "term": "30d",
    "days": 30,
    "status": "EXPIRED",
    "price": 12.5,
    "autoRenew": false,
    "nickname": null,
    "rentedAt": "2026-09-22T10:00:00.000Z",
    "expiresAt": "2026-10-22T10:00:00.000Z",
    "messageCount": 0
  }
}
```

**number.cancelled**

```json
{
  "number": {
    "id": "num_8f2k",
    "phoneNumber": "+447700900123",
    "country": "GB",
    "countryName": "United Kingdom",
    "type": "unlimited",
    "service": null,
    "serviceName": null,
    "term": "30d",
    "days": 30,
    "status": "REFUNDED",
    "price": 12.5,
    "autoRenew": false,
    "nickname": null,
    "rentedAt": "2026-09-22T10:00:00.000Z",
    "expiresAt": "2026-10-22T10:00:00.000Z",
    "messageCount": 0
  },
  "refundAmount": 12.5
}
```

**esim.allocated**

```json
{
  "order": {
    "id": "eord_9x1",
    "status": "ALLOCATED",
    "packageCode": "ES_1_7",
    "packageName": "Spain Unlimited · 7 days",
    "unlimited": true,
    "days": 7,
    "locationCode": "ES",
    "quantity": 1,
    "price": 12.1,
    "createdAt": "2026-09-22T10:00:00.000Z",
    "ready": true,
    "lifecycle": "ready",
    "profiles": [
      {
        "id": "prof_1",
        "esimTranNo": "T2026092200001",
        "iccid": "8943100000000000001",
        "activationCode": "LPA:1$rsp-eu.example.com$ABCDEF123456",
        "qrCodeUrl": "https://cdn.example.com/qr/T2026092200001.png",
        "shortUrl": null,
        "apn": "internet",
        "status": "GOT_RESOURCE",
        "smdpStatus": "RELEASED",
        "totalVolume": "1073741824",
        "usedVolume": "0",
        "activatedAt": null,
        "expiresAt": null
      }
    ]
  }
}
```

**esim.updated**

```json
{
  "order": {
    "id": "eord_9x1",
    "status": "ALLOCATED",
    "packageCode": "ES_1_7",
    "packageName": "Spain Unlimited · 7 days",
    "unlimited": true,
    "days": 7,
    "locationCode": "ES",
    "quantity": 1,
    "price": 12.1,
    "createdAt": "2026-09-22T10:00:00.000Z",
    "ready": true,
    "lifecycle": "active",
    "profiles": [
      {
        "id": "prof_1",
        "esimTranNo": "T2026092200001",
        "iccid": "8943100000000000001",
        "activationCode": "LPA:1$rsp-eu.example.com$ABCDEF123456",
        "qrCodeUrl": "https://cdn.example.com/qr/T2026092200001.png",
        "shortUrl": null,
        "apn": "internet",
        "status": "GOT_RESOURCE",
        "smdpStatus": "RELEASED",
        "totalVolume": "1073741824",
        "usedVolume": "0",
        "activatedAt": null,
        "expiresAt": null
      }
    ]
  }
}
```

**esim.failed**

```json
{
  "packageCode": "ES_1_7",
  "packageName": "Spain Unlimited · 7 days",
  "quantity": 1,
  "price": 12.1,
  "reason": "provider order rejected"
}
```

**esim.refunded**

```json
{
  "order": {
    "id": "eord_9x1",
    "status": "REFUNDED",
    "packageCode": "ES_1_7",
    "packageName": "Spain Unlimited · 7 days",
    "unlimited": true,
    "days": 7,
    "locationCode": "ES",
    "quantity": 1,
    "price": 12.1,
    "createdAt": "2026-09-22T10:00:00.000Z",
    "ready": true,
    "lifecycle": "refunded",
    "profiles": [
      {
        "id": "prof_1",
        "esimTranNo": "T2026092200001",
        "iccid": "8943100000000000001",
        "activationCode": "LPA:1$rsp-eu.example.com$ABCDEF123456",
        "qrCodeUrl": "https://cdn.example.com/qr/T2026092200001.png",
        "shortUrl": null,
        "apn": "internet",
        "status": "GOT_RESOURCE",
        "smdpStatus": "RELEASED",
        "totalVolume": "1073741824",
        "usedVolume": "0",
        "activatedAt": null,
        "expiresAt": null
      }
    ]
  },
  "refundAmount": 12.1
}
```

**ping**

```json
{
  "apiKeyLabel": "Production",
  "sentAt": "2026-09-22T10:00:00.000Z"
}
```

### Verifying signatures

`X-1001SMS-Signature` is `t=<unix seconds>,v1=<hex>` where `v1` is HMAC-SHA256 of `"<t>.<raw body>"` with the key's signing secret. Compute it over the exact bytes received, compare in constant time, reject timestamps older than five minutes. Answer 2xx within 10 seconds; failures are retried after 1 m, 5 m, 30 m, 2 h and 12 h, then marked FAILED. Twenty consecutive failed deliveries disable the webhook until the URL is saved again. Dedupe on the event `id`: a retry carries the same one.

```js
import crypto from 'node:crypto';

export function verify(rawBody, signatureHeader, secret) {
  const parts = Object.fromEntries(signatureHeader.split(',').map((p) => p.split('=')));
  const t = Number(parts.t);
  if (!t || Math.abs(Date.now() / 1000 - t) > 300) return false; // 5-minute tolerance
  const expected = crypto.createHmac('sha256', secret).update(`${t}.${rawBody}`).digest('hex');
  const a = Buffer.from(expected, 'hex');
  const b = Buffer.from(parts.v1 ?? '', 'hex');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

// Express: use express.raw({ type: 'application/json' }) so rawBody is the exact bytes.
```

### GET /webhooks/config

This key's webhook. The URL, the subscribed events, a hint of the secret and whether the endpoint is active or disabled.

- Scope: any valid key
- Docs: https://www.1001sms.com/api-docs#webhooks-config-get

**Example**

```bash
curl "https://www.1001sms.com/api/v1/webhooks/config" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "url": "https://example.com/webhooks/1001sms",
    "events": [
      "sms.received",
      "esim.allocated"
    ],
    "secretHint": "whsec_3f9a…",
    "status": "active",
    "failures": 0,
    "disabledAt": null,
    "availableEvents": [
      "sms.received",
      "number.activated",
      "number.renewed",
      "number.renewal_failed",
      "number.expiring",
      "number.expired",
      "number.cancelled",
      "esim.allocated",
      "esim.updated",
      "esim.failed",
      "esim.refunded"
    ]
  }
}
```

### PUT /webhooks/config

Set the webhook. Sets the URL and events. Saving a URL clears the failure state and re-enables a disabled endpoint; the first save returns the signing `secret` once. `url: null` removes the webhook.

- Scope: any valid key
- Docs: https://www.1001sms.com/api-docs#webhooks-config-put

**Request body (JSON)**

| Name | Type | Required | Description |
|---|---|---|---|
| `url` | string \| null | no | https URL on a public host, or null to remove. |
| `events` | string[] | no | Event names to subscribe to. |

**Example**

```bash
curl -X PUT "https://www.1001sms.com/api/v1/webhooks/config" \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/webhooks/1001sms","events":["sms.received","esim.allocated"]}'
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "url": "https://example.com/webhooks/1001sms",
    "events": [
      "sms.received",
      "esim.allocated"
    ],
    "secretHint": "whsec_3f9a…",
    "status": "active",
    "failures": 0,
    "disabledAt": null,
    "secret": "whsec_3f9a1c2b7d8e4f50a1b2c3d4e5f60718293a4b5c"
  }
}
```

### POST /webhooks/config/rotate-secret

Rotate the secret. Issues a new signing secret. The old one stops verifying at once.

- Scope: any valid key
- Docs: https://www.1001sms.com/api-docs#webhooks-rotate

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/webhooks/config/rotate-secret" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "secret": "whsec_9b8a7c6d5e4f30211a2b3c4d5e6f70819a0b1c2d"
  }
}
```

### POST /webhooks/test

Send a ping. Sends a `ping` event to the URL now and returns the delivery outcome.

- Scope: any valid key
- Docs: https://www.1001sms.com/api-docs#webhooks-test

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/webhooks/test" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "id": "dlv_01j8x",
    "status": "DELIVERED",
    "attempts": 1,
    "responseStatus": 200,
    "error": null,
    "nextAttemptAt": null
  }
}
```

### GET /webhooks/deliveries

Recent deliveries. The last deliveries to this key, newest first, each with the payload it carried. Kept for 30 days.

- Scope: any valid key
- Docs: https://www.1001sms.com/api-docs#webhooks-deliveries

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `limit` | integer | no | Up to 100, default 50. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/webhooks/deliveries" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "deliveries": [
      {
        "id": "dlv_01j8x",
        "event": "sms.received",
        "eventId": "evt_3f9a1c2b7d8e4f50a1b2",
        "status": "DELIVERED",
        "attempts": 1,
        "responseStatus": 200,
        "error": null,
        "nextAttemptAt": null,
        "deliveredAt": "2026-09-22T10:03:02.000Z",
        "createdAt": "2026-09-22T10:03:01.000Z",
        "payload": {
          "id": "evt_3f9a1c2b7d8e4f50a1b2",
          "event": "sms.received",
          "createdAt": "2026-09-22T10:03:01.000Z",
          "data": {
            "product": "virtual_number",
            "numberId": "num_8f2k",
            "phoneNumber": "+447700900123",
            "country": "GB",
            "service": "full",
            "serviceName": "All services",
            "message": {
              "id": "msg_1",
              "sender": "WhatsApp",
              "text": "Your WhatsApp code: 482-910",
              "code": "482910",
              "receivedAt": "2026-09-22T10:03:00.000Z"
            }
          }
        }
      }
    ]
  }
}
```

### POST /webhooks/deliveries/{id}/retry

Re-send a delivery. Sends one delivery again now, whatever state it is in.

- Scope: any valid key
- Docs: https://www.1001sms.com/api-docs#webhooks-retry

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | The delivery id. |

**Example**

```bash
curl -X POST "https://www.1001sms.com/api/v1/webhooks/deliveries/ID_ID/retry" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "id": "dlv_01j8x",
    "status": "DELIVERED",
    "attempts": 1,
    "responseStatus": 200,
    "error": null,
    "nextAttemptAt": null
  }
}
```

## Account

Your wallet and ledger. Every purchase, renewal and refund on any product lands in the same transaction list, with the API key that made it.

### GET /informative/balance

Balance. Quick balance check.

- Scope: `read`
- Docs: https://www.1001sms.com/api-docs#balance

**Example**

```bash
curl "https://www.1001sms.com/api/v1/informative/balance" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "balance": 12.5,
    "username": "john_doe",
    "email": "john@example.com"
  }
}
```

### GET /account/profile

Profile. Main account fields.

- Scope: `read`
- Docs: https://www.1001sms.com/api-docs#profile

**Example**

```bash
curl "https://www.1001sms.com/api/v1/account/profile" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "id": "usr_1",
    "username": "john_doe",
    "email": "john@example.com",
    "balance": 12.5,
    "createdAt": "2025-01-01T00:00:00.000Z",
    "lastLoginAt": "2026-01-20T00:00:00.000Z"
  }
}
```

### GET /account/transactions

Transactions. Paginated ledger with optional type and status filters.

- Scope: `read`
- Docs: https://www.1001sms.com/api-docs#transactions

**Query parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `page` | integer | no | Page number, default 1. |
| `limit` | integer | no | Per page, default 20, max 100. |
| `type` | string | no | DEPOSIT, SMS_PURCHASE, NUMBER_RENTAL, REFUND, WITHDRAWAL. |
| `status` | string | no | PENDING, COMPLETED, FAILED, CANCELLED. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/account/transactions" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "transactions": [
      {
        "id": "tx1",
        "amount": "10.00",
        "type": "DEPOSIT",
        "status": "COMPLETED",
        "description": null,
        "reference": null,
        "createdAt": "2026-02-01T00:00:00.000Z"
      }
    ],
    "total": 1,
    "page": 1,
    "limit": 20
  }
}
```

### GET /account/transactions/{id}

One transaction. One transaction by id.

- Scope: `read`
- Docs: https://www.1001sms.com/api-docs#transaction-by-id

**Path parameters**

| Name | Type | Required | Description |
|---|---|---|---|
| `id` | string | yes | Transaction id. |

**Example**

```bash
curl "https://www.1001sms.com/api/v1/account/transactions/ID_ID" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "id": "tx1",
    "amount": "-0.50",
    "type": "SMS_PURCHASE",
    "status": "COMPLETED",
    "description": "SMS purchase for google (US)",
    "reference": "EXTRA-12345",
    "createdAt": "2026-02-01T12:00:00.000Z"
  }
}
```

### GET /account/stats

Usage statistics. Aggregated usage across orders and transactions.

- Scope: `read`
- Docs: https://www.1001sms.com/api-docs#stats

**Example**

```bash
curl "https://www.1001sms.com/api/v1/account/stats" -H "Authorization: Bearer YOUR_API_KEY"
```

**Response** (200)

```json
{
  "success": true,
  "data": {
    "totalOrders": 48,
    "totalSpent": 23.5,
    "successfulOrders": 42,
    "successRate": 87.5,
    "totalTransactions": 60,
    "totalDeposited": 50
  }
}
```

## Statuses and lifecycles

**Activation order status**

| Status | Meaning |
|---|---|
| `RENTED` | Waiting for SMS. |
| `ACTIVE` | Active-like state in the active endpoints. |
| `PENDING` | Provider is processing. |
| `COMPLETED` | Historical state after a successful flow. |
| `EXPIRED` | Timed out; refunded when no SMS arrived. |
| `REFUNDED` | Cancelled and refunded. |
| `BLOCKED` | Blocked by the provider or the system. |

**Virtual number status**

| Status | Meaning |
|---|---|
| `ACTIVE` | Live; receives SMS until expiresAt. |
| `EXPIRED` | The term ended. Renewing is no longer possible; rent a new one. |
| `REFUNDED` | Cancelled within the window and refunded. |
| `PENDING` | Ordered, waiting for the provider to activate it. |

**eSIM order lifecycle**

| Lifecycle | Meaning |
|---|---|
| `preparing` | Ordered; no activation code yet. |
| `ready` | Activation code and QR available; not yet installed. |
| `active` | Installed or in use on a handset. |
| `expired` | Every profile is past its expiry. |
| `failed` | The provider rejected the order; refunded. |
| `cancelled` | Cancelled before activation. |
| `refunded` | Refunded by support. |
