# Requests and responses

> PS Wallet Pay API address, token authorization, HTTP methods, parameter and response formats, and the request rate limit.

All PS Wallet Pay API methods are called at a single address, authorized with the app token in a header, and respond with a JSON object containing the `ok` field.

## API address

```
https://pay.pswallet.org/api/{method}
```

Replace `{method}` with the method name, for example `https://pay.pswallet.org/api/createInvoice`.

- HTTPS only, UTF-8 encoding.
- The method name is case-insensitive: `getMe` and `getme` are the same method.

## Authorization

Pass the app token in the `PSWallet-Pay-API-Token` header:

```
PSWallet-Pay-API-Token: 1234:AbCdEf...
```

The token looks like `<app ID>:<secret>`. You can view and reissue it in the app dashboard under "API token" — see the [Security](https://help.pswallet.org/en/pay-api/security) section for details.

A request without a token, with an invalid or reissued token, or to a deleted app will receive a `401 UNAUTHORIZED` error.

## HTTP methods and parameters

- `GET` and `POST` are supported.
- Parameters are passed in the query string or in the `POST` body as `application/json` or `application/x-www-form-urlencoded`.
- Methods that create or delete data accept only `POST`. For `GET` they respond with a `405 POST_REQUIRED` error.

`POST`-only methods:

- `createInvoice`
- `deleteInvoice`
- `transfer`
- `createCheck`
- `deleteCheck`

> [!TIP]
> The easiest approach is to always call methods via `POST` with a JSON body: this works for all methods, and amounts and lists are passed without escaping.

## Response format

The response is always a JSON object with the `ok` field.

A successful response has `ok: true`, and the result is in the `result` field:

```json
{
  "ok": true,
  "result": {
    "app_id": 1234,
    "name": "My Shop",
    "payment_processing_bot_username": "pswallet"
  }
}
```

An error has `ok: false`, and the description is in the `error` field:

```json
{
  "ok": false,
  "error": { "code": 400, "name": "PARAM_AMOUNT_INVALID" }
}
```

`code` matches the HTTP status of the response, and `name` is the machine-readable error name. All codes are listed in the [Errors](https://help.pswallet.org/en/pay-api/errors) section.

## Request example

Let's create an invoice for 12.5 USDT. The `payApi` functions in the Node.js and Python examples can be used to call any method.

```bash title="cURL"
curl https://pay.pswallet.org/api/createInvoice \
  -H "PSWallet-Pay-API-Token: $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{"asset": "USDT", "amount": "12.5", "description": "Подписка на месяц"}'
```

```js title="Node.js"
// Node.js 18+ (built-in fetch)
const TOKEN = process.env.PSWALLET_PAY_TOKEN;

async function payApi(method, params = {}) {
  const response = await fetch(`https://pay.pswallet.org/api/${method}`, {
    method: "POST",
    headers: {
      "PSWallet-Pay-API-Token": TOKEN,
      "Content-Type": "application/json",
    },
    body: JSON.stringify(params),
  });
  const data = await response.json();
  if (!data.ok) {
    throw new Error(`PS Wallet Pay: ${data.error.code} ${data.error.name}`);
  }
  return data.result;
}

const invoice = await payApi("createInvoice", {
  asset: "USDT",
  amount: "12.5",
  description: "Подписка на месяц",
});
console.log(invoice.bot_invoice_url);
```

```python title="Python"
import os
import requests

TOKEN = os.environ["PSWALLET_PAY_TOKEN"]

def pay_api(method: str, params: dict | None = None):
    response = requests.post(
        f"https://pay.pswallet.org/api/{method}",
        headers={"PSWallet-Pay-API-Token": TOKEN},
        json=params or dict(),
        timeout=15,
    )
    data = response.json()
    if not data["ok"]:
        error = data["error"]
        raise RuntimeError(f"PS Wallet Pay: {error['code']} {error['name']}")
    return data["result"]

invoice = pay_api("createInvoice", {
    "asset": "USDT",
    "amount": "12.5",
    "description": "Подписка на месяц",
})
print(invoice["bot_invoice_url"])
```

## Rate limit

Each app is limited to 30 requests per second. If you exceed the limit, the API responds with a `429 TOO_MANY_REQUESTS` error: wait a moment and retry the request.

If you need to find out about invoice payments frequently, don't poll `getInvoices` in a loop — set up [webhooks](https://help.pswallet.org/en/pay-api/webhooks) instead.

## What is checked before a method is called

Before executing a method, the API checks the request step by step and returns an error at the first problem:

| Check | Error |
|---|---|
| The method exists | `405 METHOD_NOT_FOUND` |
| A method that creates or deletes data is called via `POST` | `405 POST_REQUIRED` |
| The token is provided and valid | `401 UNAUTHORIZED` |
| PS Wallet Pay is currently running | `503 TEMPORARILY_UNAVAILABLE` |
| The rate limit is not exceeded | `429 TOO_MANY_REQUESTS` |
| The request address is on the IP allowlist (if a list is set) | `403 IP_NOT_ALLOWED` |
| The app is not frozen | `403 APP_FROZEN` |

After that, the method's own parameters are checked — see the sections describing the methods.

## Retrying requests

Read methods (`getMe`, `getInvoices`, `getBalance`, and others) can be safely retried.

- `transfer` is protected from duplicate submission by the `spend_id` parameter. If a request was interrupted and you don't know whether the transfer went through, check it with `getTransfers` using the same `spend_id` — see the [Transfers](https://help.pswallet.org/en/pay-api/transfers) section for details.
- `createInvoice` and `createCheck` have no retry key: a repeated request will create another invoice or check. An extra unpaid invoice can be deleted with `deleteInvoice`, and an unactivated check with `deleteCheck`.

---
Source: https://help.pswallet.org/en/pay-api/requests
