# API reference

Base URL: `https://api.plsk.org`.

## Interfaces

| Base path | Interface | Credential |
|---|---|---|
| `/v1` | Account API. See [Account API](/api/account). | Account key |
| `/api/plesk/v2` | Plesk REST API of one subscription. See [Plesk REST API](/api/plesk). | Subscription key |
| `/api/modules/wp-toolkit/v1` | WP Toolkit REST API of one subscription. See [WP Toolkit REST API](/api/wp-toolkit). | Subscription key |
| `/mcp` | MCP server. See [MCP reference](/mcp). | OAuth access token |

Aliases: `/api/v2` is equivalent to `/api/plesk/v2`.

## Authentication

A key is sent in one of two headers:

```
X-API-Key: <key>
Authorization: Bearer <key>
```

| Credential | Prefix | Issued by | Valid for | Applies to |
|---|---|---|---|---|
| Account key | `plsa_` | Cabinet, section Agents | 1 day, 7 days or 30 days | `/v1` |
| Subscription key | `plsk_` | `POST /v1/subscriptions/{id}/keys` | 1 day, 7 days or until the subscription expires | `/api/plesk/v2`, `/api/modules/*` |
| Access token | `plso_` | OAuth 2.0 authorization code flow at `https://my.plsk.org` | at most 30 days | `/mcp` |

An account key has a scope. `read` permits listing and reading. `manage` additionally permits creating, retrying and deleting subscriptions and managing keys. `POST /v1/feedback` accepts either scope.

A subscription key is bound to one subscription and expires no later than that subscription. A subscription has at most one active key. The key value is returned once, at creation.

## Request handling

For `/api/plesk/v2` and `/api/modules/*` the gateway performs these steps:

1. Validates the key: it exists, is not revoked and not expired, and the subscription is available.
2. Applies the rate limit of the key.
3. Validates the target against the rules below.
4. Replaces the key with the Plesk credential of the subscription and forwards the request.
5. Returns the status, headers and body of the Plesk response. `Set-Cookie` and hop-by-hop headers are removed.

Path, query, method and body are forwarded unchanged. Request headers are forwarded except credentials, routing headers and hop-by-hop headers.

Targets that are not forwarded:

| Target | Result |
|---|---|
| `/api/plesk/v2/auth/...` (Plesk API keys, panel sessions) | `403 blocked_path` |
| `/api/modules/<extension>/...` for an extension other than `wp-toolkit` | `403 blocked_path` |
| Dot segments, encoded slashes or backslashes, control characters, fragments, targets longer than 8192 characters | `400 bad_path` |

## Limits

| Limit | Value |
|---|---|
| Requests per key | 120 per minute |
| Request body | 10 MiB |
| Upstream timeout | 60 s |

## Errors

Errors produced by the platform use this format:

```
{"error": {"code": "<code>", "message": "<text>"}}
```

| Status | Code | Condition |
|---|---|---|
| 400 | `bad_path` | The target is not acceptable. |
| 400 | `bad_request` | The request is malformed. |
| 401 | `invalid_key` | The key is missing, unknown, revoked or expired. The response carries `WWW-Authenticate: Bearer realm="plsk"`. |
| 403 | `blocked_path` | The target is not offered by the gateway. |
| 413 | `body_too_large` | The body exceeds 10 MiB. |
| 429 | `rate_limited` | The rate limit is exceeded. The response carries `Retry-After` in seconds. |
| 500 | `internal_error` | Internal error. |
| 502 | `upstream_unavailable` | Plesk could not be reached. |
| 504 | `upstream_timeout` | Plesk did not answer within the timeout. |

Status codes and bodies produced by Plesk are returned as received. See [Troubleshooting](/troubleshooting) for their interpretation.

## Account API conventions

- `POST /v1/subscriptions` requires the header `Idempotency-Key` (1 to 64 characters from `A-Z a-z 0-9 _ -`). A repeated request with the same key returns the existing subscription with status 200; the first request returns 201.
- `DELETE /v1/subscriptions/{id}` and `POST /v1/subscriptions/{id}/retry` return 202.
- Account API errors use the format above with the codes listed in [Account API](/api/account).
