# KRAB.GG API: Telegram Stars, Premium, GRAM and Steam

- **OpenAPI Version:** `3.1.1`
- **API Version:** `1.0.0`

Deliver **Telegram Stars**, **Premium** and **GRAM** to a `@username`, pay for channel **giveaways**, top up **Telegram Ads** accounts by link and **Steam** by login. You fund a USDT balance and send `POST /orders`, we deliver.

**Pricing.** Stars, Premium, GRAM, giveaways and Telegram Ads: the Fragment wholesale price plus a 1% fee; for `premium_giveaway` it is multiplied by the number of subscriptions. Steam: no fee, we charge exactly the dollar amount you order. `GET /price` shows the exact amount before you order.

## Quick start

1. Open the [@apistarsbot](https://t.me/apistarsbot?start=api) bot and press "Test key now": your `sk_test_…` key arrives at once. We issue a live `sk_live_…` key from the "Live access" button once we approve your request.
2. Send an order:

```bash
curl https://api.krab.gg/v1/orders \
  --request POST \
  --header 'Authorization: Bearer sk_live_…' \
  --header 'Content-Type: application/json' \
  --data '{"type":"stars","qty":100,"username":"@johnsmith","orderId":"order-123"}'
```

3. Look at `status` in the response. `completed`: delivered. `processing`: delivery is running, the outcome arrives by webhook, or ask `GET /orders/{id}`. `failed`: we could not deliver, the cause is in `reason`.

With a test key we charge and deliver nothing: the response carries the price but no `status`.

## Products

| `type`             | `qty`                                                                      | `username`                                    |
| ------------------ | -------------------------------------------------------------------------- | --------------------------------------------- |
| `stars`            | stars, 50…10,000,000                                                       | Telegram `@username`                          |
| `premium`          | months: 3, 6 or 12                                                         | Telegram `@username`                          |
| `gram`             | whole TON                                                                  | Telegram `@username`                          |
| `steam`            | dollars, cents allowed: `10.5`                                             | Steam login without `@`                       |
| `stars_giveaway`   | total Stars for the giveaway, one of the packages; winners in `winners`    | channel `@handle`                             |
| `premium_giveaway` | each subscription's length: 3, 6 or 12; as many subscriptions as `winners` | channel `@handle`                             |
| `ads_recharge`     | whole GRAM                                                                 | the "Add Funds on Fragment" link or its token |

You may drop the `@` and send the number as a string. Limits for `gram`, `ads_recharge` and `steam` move: outside them you get `min_quantity` or `max_quantity` with a `min` or `max` field.

`orderId` is your own order number, up to 128 chars from `A-Za-z0-9._:-`. Repeat a request with the same `orderId` and we return the same order without charging again. A repeat with a different `type`, `qty` or `username` gets `409 order_id_conflict`.

## Money

While delivery runs, the order amount sits in reserve on your balance. `settlement` tells you what happened to it:

- `charged`: charged, delivered;
- `pending`: reserved, delivery is running;
- `refunded`: back on your balance;
- `fail_hold`: delivery is unconfirmed and we hold the money until an operator checks it. If we charge, `order.completed` arrives; if we refund, `order.updated` arrives with `settlement: refunded`.

Retry a failed delivery with a new order and a new `orderId`, and only when the order says `retryable: true`. It appears once the money is back on your balance.

## Errors

An error comes as `{"ok": false, "error": "code", "message": "text"}`. Branch on `error` and log `message`.

- `4xx`: fix the request;
- `429`: wait as many seconds as the `Retry-After` header says. The limit is 60 requests per minute per account, and your live and test keys share one counter;
- `5xx`: retry later.

## Contacts

Base URL: `https://api.krab.gg/v1`. Responses may gain new fields; skip the ones you do not know. By issuing a live key you accept the [API partner terms](https://krab.gg/en/api-terms). Questions and disputed orders: [@heIIo\_stars](https://t.me/heIIo_stars) or <hello@krab.gg>, quote the `orderId`.

## Request identifier

Every response carries an `X-Request-Id` header (for example `req_mu88via8_53d8cd`). Store it in your own logs: it lets us find exactly the request you are asking about, with the full context of the failure. Without it we search by timestamp and address, which takes far longer.

```properties
X-Request-Id: req_mu88via8_53d8cd
```

## Servers

- **URL:** `https://api.krab.gg/v1`
  - **Description:** Production

## Operations

### Price

- **Method:** `GET`
- **Path:** `/price`
- **Tags:** Pricing and checks

How much we will charge for an order. Creates and reserves nothing; we price the order again when you create it.

#### Parameters

##### `type` required

- **In:** `query`

Product.

`string`, possible values: `"stars", "premium", "gram", "steam"`

##### `qty` required

- **In:** `query`

Same as `qty` in `POST /orders`.

`number`

#### Responses

##### Status: 200 Price

###### Content-Type: application/json

**All of:**

- **`ok`**

  `boolean`

- **`price`**

  `number` — Amount to charge in USDT dollars, up to six decimals.

- **`qty`**

  `number`

- **`type`**

  `string`

**Example:**

```json
{
  "ok": true,
  "type": "stars",
  "qty": 100,
  "price": 1.515
}
```

##### Status: 400 unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "min_quantity",
  "message": "Quantity is below the allowed minimum (see min).",
  "min": 50
}
```

##### Status: 503 Retry later: category\_disabled (sales of this product are paused), plan\_unavailable (no Premium for this term), pricing\_unavailable, rate\_unavailable (no fresh rate: Steam, or TON for GRAM).

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "rate_unavailable",
  "message": "The exchange rate is temporarily unavailable. Please retry.",
  "retryable": true
}
```

##### Status: default An error, body follows the \`Error\` schema. Besides the codes listed on the method, any request may get: - \`401\` missing\_api\_key, invalid\_api\_key: the key is missing, wrong or revoked, or access to the account is suspended or closed; - \`403\` ip\_not\_allowed: the request came from an address outside the account IP list; the response body carries \`seen\_ip\` — the address we actually saw (add exactly that one: behind NAT or a load balancer it differs from the address you see locally); - \`429\` rate\_limited: the account limit is exceeded (60 per minute by default), or one IP sent more than 120 requests per minute, and we block that address for 5 minutes. Wait \`Retry-After\` seconds; the remaining quota is in the \`X-RateLimit-Limit\` and \`X-RateLimit-Remaining\` headers; - \`400\` bad\_request, \`413\` payload\_too\_large: the body is not JSON or exceeds 32 KB; - \`404\` not\_found: no such method (wrong path or HTTP method); - \`404\` partner\_not\_found: the account was closed while the request was running; the next requests get 401; - \`500\`, \`503\`: a failure on our side or the API is stopped (service\_unavailable), retry later.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "missing_api_key",
  "message": "Missing API key. Send header 'Authorization: Bearer sk_...'."
}
```

### Recipient check

- **Method:** `GET`
- **Path:** `/check`
- **Tags:** Pricing and checks

Checks the recipient before you order, so a typo does not freeze money. Fragment checks Stars, Premium and GRAM with the same lookup delivery uses, the payment gateway checks Steam. The answer matches how an order to this recipient would end.

`accepts: false` comes with `reason`.

#### Parameters

##### `username` required

- **In:** `query`

Telegram `@username` or Steam login.

`string`

##### `type`

- **In:** `query`

Product: `stars` (default), `premium`, `gram` or `steam`.

`string`, possible values: `"stars", "premium", "gram", "steam", "stars_giveaway", "premium_giveaway", "ads_recharge"`, default: `"stars"`

##### `qty`

- **In:** `query`

Premium only: term of 3, 6 or 12, default 3.

`integer`

#### Responses

##### Status: 200 Checked

###### Content-Type: application/json

**All of:**

- **`accepts`**

  `boolean` — \`true\`: you can send the product. \`false\`: do not send it, the cause is in \`reason\`.

- **`ok`**

  `boolean`

- **`reason`**

  `string`, possible values: `"already_subscribed", "recipient_rejects"` — Only with \`accepts: false\`. \`already\_subscribed\`: the recipient already has Premium. \`recipient\_rejects\`: the recipient exists but an order to them will not go through (for Steam: the login cannot be topped up). An order to such a recipient fails with the same \`reason\`.

- **`type`**

  `string`, possible values: `"stars", "premium", "gram", "steam", "stars_giveaway", "premium_giveaway", "ads_recharge"`

**Example:**

```json
{
  "ok": true,
  "type": "stars",
  "accepts": true
}
```

##### Status: 400 username\_required, invalid\_username, unsupported\_type, invalid\_premium\_duration.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "invalid_username",
  "message": "The recipient username is invalid. Use an @handle of 5-32 characters (letters, digits, _; not starting or ending with _) for stars/premium/gram, or a Steam account login without @ (letters, digits, _ and -) for steam.",
  "type": "stars"
}
```

##### Status: 404 not\_found: no such user, or it is a channel or a bot.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "not_found",
  "message": "Not found.",
  "type": "stars"
}
```

##### Status: 502 fetch\_failed: Fragment or the Steam gateway did not answer or answered unclearly. This says nothing about the recipient, retry.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "fetch_failed",
  "message": "Temporary failure checking the recipient. Please retry.",
  "type": "stars"
}
```

##### Status: default An error, body follows the \`Error\` schema. Besides the codes listed on the method, any request may get: - \`401\` missing\_api\_key, invalid\_api\_key: the key is missing, wrong or revoked, or access to the account is suspended or closed; - \`403\` ip\_not\_allowed: the request came from an address outside the account IP list; the response body carries \`seen\_ip\` — the address we actually saw (add exactly that one: behind NAT or a load balancer it differs from the address you see locally); - \`429\` rate\_limited: the account limit is exceeded (60 per minute by default), or one IP sent more than 120 requests per minute, and we block that address for 5 minutes. Wait \`Retry-After\` seconds; the remaining quota is in the \`X-RateLimit-Limit\` and \`X-RateLimit-Remaining\` headers; - \`400\` bad\_request, \`413\` payload\_too\_large: the body is not JSON or exceeds 32 KB; - \`404\` not\_found: no such method (wrong path or HTTP method); - \`404\` partner\_not\_found: the account was closed while the request was running; the next requests get 401; - \`500\`, \`503\`: a failure on our side or the API is stopped (service\_unavailable), retry later.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "missing_api_key",
  "message": "Missing API key. Send header 'Authorization: Bearer sk_...'."
}
```

### Balance

- **Method:** `GET`
- **Path:** `/balance`
- **Tags:** Balance and deposits

Available balance in USDT dollars. The movement history lives in the cabinet.

#### Responses

##### Status: 200 Balance

###### Content-Type: application/json

**All of:**

- **`balance`**

  `number` — Available balance in USDT dollars.

- **`ok`**

  `boolean`

**Example:**

```json
{
  "ok": true,
  "balance": 42.5
}
```

##### Status: default An error, body follows the \`Error\` schema. Besides the codes listed on the method, any request may get: - \`401\` missing\_api\_key, invalid\_api\_key: the key is missing, wrong or revoked, or access to the account is suspended or closed; - \`403\` ip\_not\_allowed: the request came from an address outside the account IP list; the response body carries \`seen\_ip\` — the address we actually saw (add exactly that one: behind NAT or a load balancer it differs from the address you see locally); - \`429\` rate\_limited: the account limit is exceeded (60 per minute by default), or one IP sent more than 120 requests per minute, and we block that address for 5 minutes. Wait \`Retry-After\` seconds; the remaining quota is in the \`X-RateLimit-Limit\` and \`X-RateLimit-Remaining\` headers; - \`400\` bad\_request, \`413\` payload\_too\_large: the body is not JSON or exceeds 32 KB; - \`404\` not\_found: no such method (wrong path or HTTP method); - \`404\` partner\_not\_found: the account was closed while the request was running; the next requests get 401; - \`500\`, \`503\`: a failure on our side or the API is stopped (service\_unavailable), retry later.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "missing_api_key",
  "message": "Missing API key. Send header 'Authorization: Bearer sk_...'."
}
```

### Deposit details

- **Method:** `GET`
- **Path:** `/deposit`
- **Tags:** Balance and deposits

Where to send money. We accept only **USDT on TON**: send at least 100 USDT to `address` and **always put `memo`** in the transfer comment. The address is shared, the memo belongs to your account. We usually credit within a minute; check `GET /balance`. Forgot the memo: send the transaction hash to support and we credit it by hand.

#### Responses

##### Status: 200 Deposit details

###### Content-Type: application/json

- **`address`**

  `string` — Our wallet.

- **`asset`**

  `string`

- **`memo`**

  `string` — Transfer comment. Without it we cannot tell whose transfer it is.

- **`minRecommendedUsdt`**

  `number` — Minimum top-up amount in USDT.

- **`network`**

  `string`

- **`note`**

  `string` — Short hint, always in Russian.

- **`ok`**

  `boolean`

**Example:**

```json
{
  "ok": true,
  "asset": "USDT",
  "network": "TON",
  "address": "EQ…",
  "memo": "d1a2b3c4d5e6f7081",
  "minRecommendedUsdt": 100,
  "note": "Отправьте от 100 USDT (TON) на address с указанным memo. Без memo средства зачислить нельзя."
}
```

##### Status: 403 sandbox\_no\_deposit: the sandbox has no deposits, get live access first.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "sandbox_no_deposit",
  "message": "Deposits are not available in sandbox. Request live access first."
}
```

##### Status: default An error, body follows the \`Error\` schema. Besides the codes listed on the method, any request may get: - \`401\` missing\_api\_key, invalid\_api\_key: the key is missing, wrong or revoked, or access to the account is suspended or closed; - \`403\` ip\_not\_allowed: the request came from an address outside the account IP list; the response body carries \`seen\_ip\` — the address we actually saw (add exactly that one: behind NAT or a load balancer it differs from the address you see locally); - \`429\` rate\_limited: the account limit is exceeded (60 per minute by default), or one IP sent more than 120 requests per minute, and we block that address for 5 minutes. Wait \`Retry-After\` seconds; the remaining quota is in the \`X-RateLimit-Limit\` and \`X-RateLimit-Remaining\` headers; - \`400\` bad\_request, \`413\` payload\_too\_large: the body is not JSON or exceeds 32 KB; - \`404\` not\_found: no such method (wrong path or HTTP method); - \`404\` partner\_not\_found: the account was closed while the request was running; the next requests get 401; - \`500\`, \`503\`: a failure on our side or the API is stopped (service\_unavailable), retry later.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "missing_api_key",
  "message": "Missing API key. Send header 'Authorization: Bearer sk_...'."
}
```

### Current webhook

- **Method:** `GET`
- **Path:** `/webhook`
- **Tags:** Webhooks

The webhook URL and the `secret` for signature checks. A test key gets `secret: null`.

#### Responses

##### Status: 200 Webhook settings

###### Content-Type: application/json

- **`ok`**

  `boolean`

- **`secret`**

  `string | null`

- **`url`**

  `string | null`

**Example:**

```json
{
  "ok": true,
  "url": "https://shop.example.com/webhooks/krab",
  "secret": "whsec_4f1c…"
}
```

##### Status: default An error, body follows the \`Error\` schema. Besides the codes listed on the method, any request may get: - \`401\` missing\_api\_key, invalid\_api\_key: the key is missing, wrong or revoked, or access to the account is suspended or closed; - \`403\` ip\_not\_allowed: the request came from an address outside the account IP list; the response body carries \`seen\_ip\` — the address we actually saw (add exactly that one: behind NAT or a load balancer it differs from the address you see locally); - \`429\` rate\_limited: the account limit is exceeded (60 per minute by default), or one IP sent more than 120 requests per minute, and we block that address for 5 minutes. Wait \`Retry-After\` seconds; the remaining quota is in the \`X-RateLimit-Limit\` and \`X-RateLimit-Remaining\` headers; - \`400\` bad\_request, \`413\` payload\_too\_large: the body is not JSON or exceeds 32 KB; - \`404\` not\_found: no such method (wrong path or HTTP method); - \`404\` partner\_not\_found: the account was closed while the request was running; the next requests get 401; - \`500\`, \`503\`: a failure on our side or the API is stopped (service\_unavailable), retry later.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "missing_api_key",
  "message": "Missing API key. Send header 'Authorization: Bearer sk_...'."
}
```

### Set webhook

- **Method:** `POST`
- **Path:** `/webhook`
- **Tags:** Webhooks

Sets the URL we POST to when an order outcome changes. `url: null` turns the webhook off. The response carries `secret`: it appears with the first URL and stays the same when you change the URL. The "Rotate secret" button in the cabinet issues a new one, and the old one stops working at once.

**Event.** JSON with `event`, `orderId`, `status`, `type`, `qty`, `username`, `settlement`, `sequence` and `occurredAt` (unix time in seconds when we recorded this state), plus `reason` and `retryable` when a delivery attempt failed. An event arrives when the order outcome changes: `order.completed` (delivered), `order.failed` (not delivered, or closed without delivery) or `order.updated` (same outcome, but `settlement`, `reason` or `retryable` changed, for example the reserve was returned after `fail_hold`). `sequence` grows by 1 with each event of an order: if an event has a lower number than one you already processed, skip it. The "Send test" button in the cabinet sends `{"event":"ping","at":<unix time>}` with `X-Webhook-Id: test`.

**Signature.** We send `X-Webhook-Id`, `X-Webhook-Timestamp` (unix time in seconds) and `X-Webhook-Signature: v1,<signature>`, where signature = base64(HMAC-SHA256(`id.timestamp.body`, secret)) and `body` is the raw request body. Reject deliveries older than a few minutes.

```js
const expected = Buffer.from(crypto.createHmac('sha256', secret).update(`${id}.${ts}.${rawBody}`).digest('base64'));
const got = Buffer.from(signature.replace(/^v1,/, ''));
const ok = got.length === expected.length && crypto.timingSafeEqual(expected, got);
```

**Delivery.** Answer 2xx within 10 seconds with a body of at most 64 KB, otherwise the attempt fails. We do not follow redirects: a 3xx answer is a failure too. We retry after 1m→5m→30m→2h, 5 attempts in total, then notify you in the cabinet. One event can arrive twice: `X-Webhook-Id` stays the same across retries, so drop duplicates by it. Delivery order is not guaranteed, so compare `sequence`. An event not delivered in 5 attempts or missed while the webhook was off may never arrive (after you turn the webhook on, events for orders changed in the last day may arrive): always reconcile the final status via `GET /orders/{id}`.

#### Request Body

##### Content-Type: application/json

- **`url` (required)**

  `string | null` — Public https or http URL. \`null\` turns the webhook off. If the host name resolves to a private network, deliveries to it fail.

**Example:**

```json
{
  "url": "https://shop.example.com/webhooks/krab"
}
```

#### Responses

##### Status: 200 Saved

###### Content-Type: application/json

- **`ok`**

  `boolean`

- **`secret`**

  `string | null`

- **`url`**

  `string | null`

**Example:**

```json
{
  "ok": true,
  "url": "https://shop.example.com/webhooks/krab",
  "secret": "whsec_4f1c…"
}
```

##### Status: 400 url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "private_address_not_allowed",
  "message": "The webhook URL must be a public address."
}
```

##### Status: 403 test\_key\_readonly: a test key cannot change settings, use a live key.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "test_key_readonly",
  "message": "A test key (sk_test_) cannot change configuration. Use a live key (sk_live_)."
}
```

##### Status: default An error, body follows the \`Error\` schema. Besides the codes listed on the method, any request may get: - \`401\` missing\_api\_key, invalid\_api\_key: the key is missing, wrong or revoked, or access to the account is suspended or closed; - \`403\` ip\_not\_allowed: the request came from an address outside the account IP list; the response body carries \`seen\_ip\` — the address we actually saw (add exactly that one: behind NAT or a load balancer it differs from the address you see locally); - \`429\` rate\_limited: the account limit is exceeded (60 per minute by default), or one IP sent more than 120 requests per minute, and we block that address for 5 minutes. Wait \`Retry-After\` seconds; the remaining quota is in the \`X-RateLimit-Limit\` and \`X-RateLimit-Remaining\` headers; - \`400\` bad\_request, \`413\` payload\_too\_large: the body is not JSON or exceeds 32 KB; - \`404\` not\_found: no such method (wrong path or HTTP method); - \`404\` partner\_not\_found: the account was closed while the request was running; the next requests get 401; - \`500\`, \`503\`: a failure on our side or the API is stopped (service\_unavailable), retry later.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "missing_api_key",
  "message": "Missing API key. Send header 'Authorization: Bearer sk_...'."
}
```

### Create order

- **Method:** `POST`
- **Path:** `/orders`
- **Tags:** Orders

Creates the order and sends the product right away. We usually deliver Stars, Premium and GRAM within seconds, and the response already says `status: completed`. Steam usually answers `processing`: the outcome arrives by webhook or via `GET /orders/{id}`. If delivery fails at once, you get `status: failed` with the cause in `reason`. If the Steam gateway runs out of funds and the order never reached it, the order waits up to two hours, then ends `failed` with `reason: temporary` and the money returns to your balance. If the transfer may have gone through, an operator decides.

Repeating a request with the same `orderId` is safe: we return the same order and do not charge again. After `503 order_create_failed`, retry the same `orderId`: a failed new creation rolls back its reserve, so it does not leave funds held without an order. A legacy reservation without recorded order terms returns `409 hold_requires_resolution`; contact support to reconcile or release it. A closed reservation still returns `hold_settled`, and one with a different amount returns `hold_amount_mismatch`.

#### Request Body

##### Content-Type: application/json

- **`orderId` (required)**

  `string` — Your order number, up to 128 chars from \`A-Za-z0-9.\_:-\`. Repeating the same orderId returns the existing order and does not charge again; with a different \`type\`, \`qty\` or \`username\` you get 409 \`order\_id\_conflict\`.

- **`qty` (required)**

  `number` — How much: stars, Premium months (3, 6, 12), whole TON for gram or dollars for steam. For \`stars\_giveaway\` it is the TOTAL number of stars (one of Fragment packages, see \`allowed\` in the error): the platform splits them between winners. For \`premium\_giveaway\` it is the term of EACH subscription (3, 6, 12), and you buy as many subscriptions as there are winners. For \`ads\_recharge\` — whole GRAM.

- **`type` (required)**

  `string`, possible values: `"stars", "premium", "gram", "steam", "stars_giveaway", "premium_giveaway", "ads_recharge"` — Product: \`stars\`, \`premium\`, \`gram\`, \`steam\`, \`stars\_giveaway\`, \`premium\_giveaway\` or \`ads\_recharge\`.

- **`username` (required)**

  `string` — Recipient: a Telegram \`@username\`, or a Steam account login for steam. For giveaways — a CHANNEL \`@handle\` (a giveaway cannot run in a bot or a personal account). For \`ads\_recharge\` — the token from the Telegram Ads link "Add Funds on Fragment" (\`fragment.com/ads/pay?account=…\`); the whole link is accepted too. It is not a @username and not a numeric id.

- **`dryRun`**

  `boolean` — \`true\`: sandbox. We only price the order: no charge, no delivery, no order. A test key always works this way. The response has the price, \`dryRun: true\` and \`mode\`, but no \`status\` or \`settlement\`; \`GET /orders/{id}\` for that orderId returns 404 and no webhooks fire. Besides \`true\`/\`false\` we accept \`1\`/\`0\` and the strings \`"true"\`/\`"false"\`; any other value → 400 \`invalid\_dry\_run\`.

- **`winners`**

  `integer` — Giveaways only: how many people share the prize. For \`stars\_giveaway\` the cap depends on the amount (Fragment gives at least 100 stars per person, so no more than \`qty / 100\` winners), for \`premium\_giveaway\` — up to 24000 subscriptions. Other products do not accept this field.

**Example:**

```json
{
  "type": "stars",
  "qty": 100,
  "winners": 5,
  "username": "@johnsmith",
  "orderId": "order-123",
  "dryRun": false
}
```

#### Responses

##### Status: 200 The order. In the sandbox (test key or \`dryRun: true\`) no order is created: \`status\` and \`settlement\` are absent, and \`dryRun: true\` and \`mode\` come instead.

###### Content-Type: application/json

**All of:**

- **`dryRun`**

  `boolean` — Sandbox only: \`true\`.

- **`idempotent`**

  `boolean` — \`true\`: this orderId already existed, we returned that order and charged nothing.

- **`mode`**

  `string`, possible values: `"live", "test"` — Sandbox only: \`test\` for a test key, \`live\` for a live key with dryRun.

- **`ok`**

  `boolean`

- **`orderId`**

  `string`

- **`price`**

  `number` — Price in USDT dollars. Only in the \`POST /orders\` response.

- **`qty`**

  `number` — For steam, dollars rounded to the cent.

- **`reason`**

  `string | null`, possible values: `"already_subscribed", "partial_delivery", "recipient_not_found", "recipient_rejects", "unsupported_type", "under_review", "temporary"` — Why delivery failed. Comes with \`retryable\`, including when the order later closed as \`cancelled\` or \`refunded\`. - \`recipient\_not\_found\`: no such recipient; - \`recipient\_rejects\`: the recipient cannot take this product; - \`already\_subscribed\`: the recipient already has Premium; - \`unsupported\_type\`: we cannot deliver this product right now; - \`temporary\`: a failure before sending; retry with a new orderId once \`retryable: true\`; - \`under\_review\`: the transfer may have gone through, we are checking, do not create a new order; - \`partial\_delivery\`: we delivered part and will finish the rest by hand, do not retry. For \`recipient\_not\_found\`, \`recipient\_rejects\`, \`already\_subscribed\` and \`unsupported\_type\` the money returns on its own, usually within a minute. \`settlement\` shows where the money is now.

- **`retryable`**

  `boolean` — \`true\`: the money for this order is back on your balance, retry with a new orderId. \`false\`: a retry will not help, or the money is still reserved and delivery may still finish.

- **`settlement`**

  `string`, possible values: `"charged", "refunded", "fail_hold", "pending"` — What happened to the money: \`charged\`, \`pending\`, \`refunded\` or \`fail\_hold\` (see "Money" in the introduction). Sandbox responses omit it.

- **`status`**

  `string`, possible values: `"completed", "processing", "failed", "refunded", "cancelled"` — \`completed\`: delivered. \`processing\`: delivery is running. \`failed\`: delivery failed. \`cancelled\`, \`refunded\`: the order closed without delivery. Sandbox responses omit this field.

- **`type`**

  `string`

- **`username`**

  `string` — As you sent it in the order.

**Example:**

```json
{
  "ok": true,
  "orderId": "order-123",
  "status": "completed",
  "type": "stars",
  "qty": 100,
  "username": "@johnsmith",
  "price": 1.515,
  "settlement": "charged"
}
```

##### Status: 400 Fix the request: username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, invalid\_dry\_run. hold\_settled, hold\_amount\_mismatch: this orderId is taken, use a new one.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "order_id_required",
  "message": "An orderId is required."
}
```

##### Status: 402 insufficient\_funds: not enough money. The response has \`balance\` and \`required\` in dollars.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "insufficient_funds",
  "message": "Insufficient balance for this order (see balance and required).",
  "balance": 0.5,
  "required": 1.515
}
```

##### Status: 409 in\_progress: a parallel request is processing this orderId; retry in a second, no duplicate appears. order\_id\_conflict: this orderId already belongs to an order with a different \`type\`, \`qty\` or \`username\`; use a new one. hold\_requires\_resolution: an existing reservation has no matching recorded terms; contact support to reconcile or release it. No new order or delivery is started.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "order_id_conflict",
  "message": "This orderId already belongs to a different order (type, quantity or recipient). Use a new orderId."
}
```

##### Status: 503 Retry later: category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "order_create_failed",
  "message": "Could not create the order. Please retry."
}
```

##### Status: default An error, body follows the \`Error\` schema. Besides the codes listed on the method, any request may get: - \`401\` missing\_api\_key, invalid\_api\_key: the key is missing, wrong or revoked, or access to the account is suspended or closed; - \`403\` ip\_not\_allowed: the request came from an address outside the account IP list; the response body carries \`seen\_ip\` — the address we actually saw (add exactly that one: behind NAT or a load balancer it differs from the address you see locally); - \`429\` rate\_limited: the account limit is exceeded (60 per minute by default), or one IP sent more than 120 requests per minute, and we block that address for 5 minutes. Wait \`Retry-After\` seconds; the remaining quota is in the \`X-RateLimit-Limit\` and \`X-RateLimit-Remaining\` headers; - \`400\` bad\_request, \`413\` payload\_too\_large: the body is not JSON or exceeds 32 KB; - \`404\` not\_found: no such method (wrong path or HTTP method); - \`404\` partner\_not\_found: the account was closed while the request was running; the next requests get 401; - \`500\`, \`503\`: a failure on our side or the API is stopped (service\_unavailable), retry later.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "missing_api_key",
  "message": "Missing API key. Send header 'Authorization: Bearer sk_...'."
}
```

### Order status

- **Method:** `GET`
- **Path:** `/orders/{id}`
- **Tags:** Orders

The order by your `orderId`: `status`, `settlement`, plus `reason` and `retryable` after a failed delivery. No price here; it comes in the `POST /orders` response.

#### Parameters

##### `id` required

- **In:** `path`

Your orderId.

`string`

#### Responses

##### Status: 200 Order

###### Content-Type: application/json

**All of:**

- **`dryRun`**

  `boolean` — Sandbox only: \`true\`.

- **`idempotent`**

  `boolean` — \`true\`: this orderId already existed, we returned that order and charged nothing.

- **`mode`**

  `string`, possible values: `"live", "test"` — Sandbox only: \`test\` for a test key, \`live\` for a live key with dryRun.

- **`ok`**

  `boolean`

- **`orderId`**

  `string`

- **`price`**

  `number` — Price in USDT dollars. Only in the \`POST /orders\` response.

- **`qty`**

  `number` — For steam, dollars rounded to the cent.

- **`reason`**

  `string | null`, possible values: `"already_subscribed", "partial_delivery", "recipient_not_found", "recipient_rejects", "unsupported_type", "under_review", "temporary"` — Why delivery failed. Comes with \`retryable\`, including when the order later closed as \`cancelled\` or \`refunded\`. - \`recipient\_not\_found\`: no such recipient; - \`recipient\_rejects\`: the recipient cannot take this product; - \`already\_subscribed\`: the recipient already has Premium; - \`unsupported\_type\`: we cannot deliver this product right now; - \`temporary\`: a failure before sending; retry with a new orderId once \`retryable: true\`; - \`under\_review\`: the transfer may have gone through, we are checking, do not create a new order; - \`partial\_delivery\`: we delivered part and will finish the rest by hand, do not retry. For \`recipient\_not\_found\`, \`recipient\_rejects\`, \`already\_subscribed\` and \`unsupported\_type\` the money returns on its own, usually within a minute. \`settlement\` shows where the money is now.

- **`retryable`**

  `boolean` — \`true\`: the money for this order is back on your balance, retry with a new orderId. \`false\`: a retry will not help, or the money is still reserved and delivery may still finish.

- **`settlement`**

  `string`, possible values: `"charged", "refunded", "fail_hold", "pending"` — What happened to the money: \`charged\`, \`pending\`, \`refunded\` or \`fail\_hold\` (see "Money" in the introduction). Sandbox responses omit it.

- **`status`**

  `string`, possible values: `"completed", "processing", "failed", "refunded", "cancelled"` — \`completed\`: delivered. \`processing\`: delivery is running. \`failed\`: delivery failed. \`cancelled\`, \`refunded\`: the order closed without delivery. Sandbox responses omit this field.

- **`type`**

  `string`

- **`username`**

  `string` — As you sent it in the order.

**Example:**

```json
{
  "ok": true,
  "orderId": "order-123",
  "status": "completed",
  "type": "stars",
  "qty": 100,
  "username": "@johnsmith",
  "settlement": "charged"
}
```

##### Status: 400 invalid\_order\_id: malformed orderId.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "invalid_order_id",
  "message": "The orderId format is invalid. Use up to 128 characters from A-Z, a-z, 0-9, dot, underscore, colon or hyphen."
}
```

##### Status: 404 not\_found: no such order. Sandbox orders are not stored.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "not_found",
  "message": "Not found."
}
```

##### Status: default An error, body follows the \`Error\` schema. Besides the codes listed on the method, any request may get: - \`401\` missing\_api\_key, invalid\_api\_key: the key is missing, wrong or revoked, or access to the account is suspended or closed; - \`403\` ip\_not\_allowed: the request came from an address outside the account IP list; the response body carries \`seen\_ip\` — the address we actually saw (add exactly that one: behind NAT or a load balancer it differs from the address you see locally); - \`429\` rate\_limited: the account limit is exceeded (60 per minute by default), or one IP sent more than 120 requests per minute, and we block that address for 5 minutes. Wait \`Retry-After\` seconds; the remaining quota is in the \`X-RateLimit-Limit\` and \`X-RateLimit-Remaining\` headers; - \`400\` bad\_request, \`413\` payload\_too\_large: the body is not JSON or exceeds 32 KB; - \`404\` not\_found: no such method (wrong path or HTTP method); - \`404\` partner\_not\_found: the account was closed while the request was running; the next requests get 401; - \`500\`, \`503\`: a failure on our side or the API is stopped (service\_unavailable), retry later.

###### Content-Type: application/json

**All of:**

- **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

- **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

- **`ok` (required)**

  `boolean`

- **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

- **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

- **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

- **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

- **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

- **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

- **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

- **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

- **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

- **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "missing_api_key",
  "message": "Missing API key. Send header 'Authorization: Bearer sk_...'."
}
```

## Schemas

### Error

- **Type:**`object`

* **`error` (required)**

  `string` — Error code. 400, fix the request: unsupported\_type, invalid\_quantity, min\_quantity, max\_quantity, invalid\_premium\_duration, username\_required, invalid\_username, order\_id\_required, invalid\_order\_id, invalid\_dry\_run, hold\_settled, hold\_amount\_mismatch, url\_required, invalid\_url, unsupported\_protocol, private\_address\_not\_allowed, invalid\_stars\_package, winners\_required, invalid\_winners, winners\_not\_supported, recipient\_not\_channel, recipient\_not\_found, invalid\_ads\_account, ads\_account\_not\_found, fragment\_rejected, bad\_request. 401: missing\_api\_key, invalid\_api\_key. 402: insufficient\_funds. 403: ip\_not\_allowed, test\_key\_readonly, sandbox\_no\_deposit. 404: not\_found, partner\_not\_found. 409: in\_progress, order\_id\_conflict, hold\_requires\_resolution. 413: payload\_too\_large. 429: rate\_limited. 502: fetch\_failed. 503, retry later: service\_unavailable, category\_disabled, plan\_unavailable, pricing\_unavailable, rate\_unavailable, order\_create\_failed, hold\_failed. 500, retry: internal\_error, auth\_error, tenant\_lookup\_failed.

* **`message` (required)**

  `string` — English phrase for logs. Build your logic on \`error\`.

* **`ok` (required)**

  `boolean`

* **`allowed`**

  `array` — Allowed Premium terms. Comes with \`invalid\_premium\_duration\`.

  **Items:**

  `integer`

* **`balance`**

  `number` — Your balance in dollars. Comes with \`insufficient\_funds\`.

* **`max`**

  `number` — Upper bound for \`qty\`. Comes with \`max\_quantity\`.

* **`min`**

  `number` — Lower bound for \`qty\`. Comes with \`min\_quantity\`.

* **`required`**

  `number` — Amount the order needs, in dollars. Comes with \`insufficient\_funds\`.

* **`retryable`**

  `boolean` — Comes with \`in\_progress\` and \`rate\_unavailable\`: you can retry the request.

* **`retryAfter`**

  `integer` — Seconds to wait before retrying. Comes with \`rate\_limited\`.

* **`step`**

  `number` — Comes with \`invalid\_quantity\` when a steam amount has more than two decimals: the amount must be in whole cents.

* **`type`**

  `string` — Product the check ran for. Comes with \`GET /check\` recipient errors: invalid\_username, invalid\_premium\_duration, not\_found, fetch\_failed.

* **`whole`**

  `boolean` — Comes with \`invalid\_quantity\` when stars, premium or gram got a fractional \`qty\`: a whole number is required.

**Example:**

```json
{
  "ok": false,
  "error": "min_quantity",
  "message": "Quantity is below the allowed minimum (see min).",
  "min": 50
}
```

### Price

- **Type:**`object`

* **`ok`**

  `boolean`

* **`price`**

  `number` — Amount to charge in USDT dollars, up to six decimals.

* **`qty`**

  `number`

* **`type`**

  `string`

**Example:**

```json
{
  "ok": true,
  "type": "stars",
  "qty": 100,
  "price": 1.515
}
```

### Recipient

- **Type:**`object`

* **`accepts`**

  `boolean` — \`true\`: you can send the product. \`false\`: do not send it, the cause is in \`reason\`.

* **`ok`**

  `boolean`

* **`reason`**

  `string`, possible values: `"already_subscribed", "recipient_rejects"` — Only with \`accepts: false\`. \`already\_subscribed\`: the recipient already has Premium. \`recipient\_rejects\`: the recipient exists but an order to them will not go through (for Steam: the login cannot be topped up). An order to such a recipient fails with the same \`reason\`.

* **`type`**

  `string`, possible values: `"stars", "premium", "gram", "steam", "stars_giveaway", "premium_giveaway", "ads_recharge"`

**Example:**

```json
{
  "ok": true,
  "type": "stars",
  "accepts": true
}
```

### Balance

- **Type:**`object`

* **`balance`**

  `number` — Available balance in USDT dollars.

* **`ok`**

  `boolean`

**Example:**

```json
{
  "ok": true,
  "balance": 42.5
}
```

### OrderRequest

- **Type:**`object`

* **`orderId` (required)**

  `string` — Your order number, up to 128 chars from \`A-Za-z0-9.\_:-\`. Repeating the same orderId returns the existing order and does not charge again; with a different \`type\`, \`qty\` or \`username\` you get 409 \`order\_id\_conflict\`.

* **`qty` (required)**

  `number` — How much: stars, Premium months (3, 6, 12), whole TON for gram or dollars for steam. For \`stars\_giveaway\` it is the TOTAL number of stars (one of Fragment packages, see \`allowed\` in the error): the platform splits them between winners. For \`premium\_giveaway\` it is the term of EACH subscription (3, 6, 12), and you buy as many subscriptions as there are winners. For \`ads\_recharge\` — whole GRAM.

* **`type` (required)**

  `string`, possible values: `"stars", "premium", "gram", "steam", "stars_giveaway", "premium_giveaway", "ads_recharge"` — Product: \`stars\`, \`premium\`, \`gram\`, \`steam\`, \`stars\_giveaway\`, \`premium\_giveaway\` or \`ads\_recharge\`.

* **`username` (required)**

  `string` — Recipient: a Telegram \`@username\`, or a Steam account login for steam. For giveaways — a CHANNEL \`@handle\` (a giveaway cannot run in a bot or a personal account). For \`ads\_recharge\` — the token from the Telegram Ads link "Add Funds on Fragment" (\`fragment.com/ads/pay?account=…\`); the whole link is accepted too. It is not a @username and not a numeric id.

* **`dryRun`**

  `boolean` — \`true\`: sandbox. We only price the order: no charge, no delivery, no order. A test key always works this way. The response has the price, \`dryRun: true\` and \`mode\`, but no \`status\` or \`settlement\`; \`GET /orders/{id}\` for that orderId returns 404 and no webhooks fire. Besides \`true\`/\`false\` we accept \`1\`/\`0\` and the strings \`"true"\`/\`"false"\`; any other value → 400 \`invalid\_dry\_run\`.

* **`winners`**

  `integer` — Giveaways only: how many people share the prize. For \`stars\_giveaway\` the cap depends on the amount (Fragment gives at least 100 stars per person, so no more than \`qty / 100\` winners), for \`premium\_giveaway\` — up to 24000 subscriptions. Other products do not accept this field.

**Example:**

```json
{
  "type": "stars",
  "qty": 100,
  "winners": 5,
  "username": "@johnsmith",
  "orderId": "order-123",
  "dryRun": false
}
```

### Order

- **Type:**`object`

* **`dryRun`**

  `boolean` — Sandbox only: \`true\`.

* **`idempotent`**

  `boolean` — \`true\`: this orderId already existed, we returned that order and charged nothing.

* **`mode`**

  `string`, possible values: `"live", "test"` — Sandbox only: \`test\` for a test key, \`live\` for a live key with dryRun.

* **`ok`**

  `boolean`

* **`orderId`**

  `string`

* **`price`**

  `number` — Price in USDT dollars. Only in the \`POST /orders\` response.

* **`qty`**

  `number` — For steam, dollars rounded to the cent.

* **`reason`**

  `string | null`, possible values: `"already_subscribed", "partial_delivery", "recipient_not_found", "recipient_rejects", "unsupported_type", "under_review", "temporary"` — Why delivery failed. Comes with \`retryable\`, including when the order later closed as \`cancelled\` or \`refunded\`. - \`recipient\_not\_found\`: no such recipient; - \`recipient\_rejects\`: the recipient cannot take this product; - \`already\_subscribed\`: the recipient already has Premium; - \`unsupported\_type\`: we cannot deliver this product right now; - \`temporary\`: a failure before sending; retry with a new orderId once \`retryable: true\`; - \`under\_review\`: the transfer may have gone through, we are checking, do not create a new order; - \`partial\_delivery\`: we delivered part and will finish the rest by hand, do not retry. For \`recipient\_not\_found\`, \`recipient\_rejects\`, \`already\_subscribed\` and \`unsupported\_type\` the money returns on its own, usually within a minute. \`settlement\` shows where the money is now.

* **`retryable`**

  `boolean` — \`true\`: the money for this order is back on your balance, retry with a new orderId. \`false\`: a retry will not help, or the money is still reserved and delivery may still finish.

* **`settlement`**

  `string`, possible values: `"charged", "refunded", "fail_hold", "pending"` — What happened to the money: \`charged\`, \`pending\`, \`refunded\` or \`fail\_hold\` (see "Money" in the introduction). Sandbox responses omit it.

* **`status`**

  `string`, possible values: `"completed", "processing", "failed", "refunded", "cancelled"` — \`completed\`: delivered. \`processing\`: delivery is running. \`failed\`: delivery failed. \`cancelled\`, \`refunded\`: the order closed without delivery. Sandbox responses omit this field.

* **`type`**

  `string`

* **`username`**

  `string` — As you sent it in the order.

**Example:**

```json
{
  "ok": true,
  "orderId": "order-123",
  "status": "completed",
  "type": "stars",
  "qty": 100,
  "username": "@johnsmith",
  "price": 1.515,
  "settlement": "charged"
}
```
