# piv.day API Reference

Full reference for the piv.day REST API: every endpoint, request body, response shape, error code and webhook. Exported from https://piv.day/api/docs for use with AI assistants.

---

## Getting started

REST API на тарифі Business. Bearer-токени, JSON, ідемпотентні ендпойнти, передбачувані коди помилок. Документація нижче описує кожен ендпойнт з прикладом запиту та відповіді.

### Base URL

```
https://app.piv.day/api/v1
```

Всі ендпойнти розміщені під цим префіксом. Одне середовище — окремих staging-хостів немає.

### Автентифікація

```
Authorization: Bearer YOUR_API_KEY
```

Ключі створюються в дашборді в розділі `Налаштування → API Keys`. Кожен ключ має гранульовані дозволи (читання номерів, відправлення SMS, купівля проксі тощо). Скомпрометований токен ротується одним кліком без повторного онбордингу команди.

### Акаунт

**200 OK**
```
curl https://app.piv.day/api/v1/account \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**Один службовий ендпойнт — баланс і email поточного акаунту. Зручно перевірити, що ключ робочий і автентифікація налаштована правильно.**
```
{
  "success": true,
  "data": {
    "user_id": "usr-uuid-...",
    "balance": 150.50,
    "email": "u***@example.com",
    "team_id": null,
    "is_team_member": false
  }
}
```

GET /api/v1/account

### Формат відповіді

```
{
  "success": true,
  "data": { ... }
}
```

Всі відповіді — JSON. При помилці `success` = `false`, а тіло містить `error.code` і `error.message`.

### Rate limits

**100 запитів на хвилину на кожен API-ключ. Ліміт спільний для всіх ендпойнтів. Якщо досягли ліміту — пишіть у Telegram-підтримку, збільшимо під навантаження без тривалих погоджень.

При перевищенні приходить `429 RATE_LIMITED` і заголовок `Retry-After` — скільки секунд чекати до наступного запиту.**
```
HTTP/1.1 429 Too Many Requests
Retry-After: 12

{
  "success": false,
  "error": {
    "code": "RATE_LIMITED",
    "message": "Rate limit exceeded, retry in 12s"
  }
}
```

429 Too Many Requests

## Номери

Купівля, продовження, відновлення номерів. Приймання та надсилання SMS. Запуск Google QR-верифікації. Підписка на події через вебхуки.

### Список країн і цін

`GET /api/v1/numbers/countries`

Повертає країни, доступні для купівлі, з актуальною ціною та напрямками SMS. Усі ціни є підсумковими з урахуванням знижки вашої підписки.

**Response fields**

| Field | Type | Description |
| --- | --- | --- |
| `country_code` | string | Двобуквений ISO-код країни (наприклад `SE`). |
| `price_per_month` | number | Підсумкова ціна для вашого тарифу — те, що реально списується при купівлі. Якщо у вас Premium або Business, знижка вже включена. |
| `base_price` | number | Ціна без знижок (тариф Free). Повертається для порівняння — щоб бачити, скільки заощаджує підписка. |
| `can_send_sms` | boolean | Чи можна з цього номера надсилати вихідні SMS. |
| `can_receive_sms` | boolean | Чи можна приймати вхідні SMS. |
| `sms_send_price` | number\|null | Ціна за вихідне SMS — також з урахуванням тарифу. `null`, якщо відправка з цієї країни недоступна. |

**curl**
```bash
curl https://app.piv.day/api/v1/numbers/countries \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": [
    {
      "country_code": "SE",
      "price_per_month": 4.00,
      "base_price": 5.00,
      "can_send_sms": true,
      "can_receive_sms": true,
      "sms_send_price": 0.25
    },
    {
      "country_code": "GB",
      "price_per_month": 3.00,
      "base_price": 3.00,
      "can_send_sms": true,
      "can_receive_sms": true,
      "sms_send_price": 0.20
    }
  ]
}
```

### Список номерів акаунту

`GET /api/v1/numbers`

Повертає сторінку номерів акаунту з фільтрами за країною, статусом і пошуком по номеру або кастомному імені.

**Query parameters**

| Field | Type | Description |
| --- | --- | --- |
| `limit` | integer | Кількість записів на сторінці (за замовчуванням 50, максимум 200). |
| `offset` | integer | Зміщення для пагінації. |
| `country` | string | ISO-код країни для фільтра. |
| `status` | string | Один із `active`, `expired`, `pending_restore`. |
| `search` | string | Підрядок для пошуку по номеру або кастомному імені. |

**curl**
```bash
curl "https://app.piv.day/api/v1/numbers?country=SE&status=active&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "numbers": [
      {
        "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
        "phone_number": "+46764794425",
        "country_code": "SE",
        "status": "active",
        "created_at": "2026-05-01T10:00:00Z",
        "expires_at": "2026-05-31T10:00:00Z",
        "auto_renew": false,
        "custom_name": "Office line",
        "purchased_at": "2026-05-01T10:00:00Z",
        "next_renewal_date": "2026-05-31T10:00:00Z",
        "tags": ["support"],
        "can_send_sms": true,
        "can_receive_sms": true
      }
    ],
    "pagination": { "total": 1, "limit": 10, "offset": 0 }
  }
}
```

### Один номер за ID

`GET /api/v1/numbers/{piv_num_id}`

Повна картка номера: статус, дати, авто-продовження, теги, дозволені SMS-напрямки.

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `NUMBER_NOT_FOUND` | 404 | Номер не знайдено або він не належить акаунту. |

**curl**
```bash
curl https://app.piv.day/api/v1/numbers/vzPA1-kHKSg-EAL7e-Jqd3o \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
    "phone_number": "+46764794425",
    "country_code": "SE",
    "status": "active",
    "created_at": "2026-05-01T10:00:00Z",
    "expires_at": "2026-05-31T10:00:00Z",
    "auto_renew": false,
    "custom_name": "Office line",
    "purchased_at": "2026-05-01T10:00:00Z",
    "next_renewal_date": "2026-05-31T10:00:00Z",
    "tags": ["support"],
    "can_send_sms": true,
    "can_receive_sms": true
  }
}
```

### Купівля номера

`POST /api/v1/numbers/purchase`

Купує один номер обраної країни на вказаний термін. Сума списується з балансу акаунту. Повертає `piv_num_id`, який далі використовується у всіх операціях з номером.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `country_code` * | string | ISO-код країни. |
| `duration_months` * | integer | Термін оренди в місяцях (мінімум 1). |
| `auto_renew` * | boolean | Увімкнути авто-продовження одразу після купівлі. |
| `custom_name` | string | Необов'язкове людинозрозуміле ім'я для номера. |

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `COUNTRY_NOT_AVAILABLE` | 400 | Країна недоступна або для неї немає цін. |
| `NO_NUMBERS_AVAILABLE` | 400 | У вказаній країні зараз немає вільних номерів. |
| `INSUFFICIENT_BALANCE` | 402 | На балансі недостатньо коштів для купівлі. |
| `VALIDATION_ERROR` | 400 | Невалідні поля в тілі запиту. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/numbers/purchase \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "country_code": "SE",
    "duration_months": 1,
    "auto_renew": false,
    "custom_name": "My Sweden Number"
  }'
```

**200 OK**
```json
{
  "success": true,
  "cost": 5.00,
  "numbers": [
    {
      "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
      "country_code": "SE",
      "phone_number": "+46764794425",
      "created_at": "2026-05-01T10:00:00Z",
      "expires_at": "2026-05-31T10:00:00Z",
      "auto_renew": false,
      "custom_name": "My Sweden Number"
    }
  ]
}
```

### Продовжити номер

`POST /api/v1/numbers/{piv_num_id}/renew`

Продовження активного номера на вказаний термін. Вартість списується з балансу в момент виклику.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `duration_months` * | integer | На скільки місяців продовжити. |

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `NUMBER_NOT_FOUND` | 404 | Номер не знайдено або він не належить акаунту. |
| `COUNTRY_NOT_AVAILABLE` | 400 | Для країни номера не знайдено цін. |
| `INSUFFICIENT_BALANCE` | 402 | Недостатньо коштів для продовження. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/numbers/vzPA1-kHKSg-EAL7e-Jqd3o/renew \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "duration_months": 1 }'
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
    "phone_number": "+46764794425",
    "old_expires_at": "2026-05-31T10:00:00Z",
    "new_expires_at": "2026-06-30T10:00:00Z",
    "cost": 5.00
  }
}
```

### Оновити номер

`PATCH /api/v1/numbers/{piv_num_id}`

Наразі редагується лише кастомне ім'я номера.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `custom_name` * | string | Нове ім'я номера. |

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `NUMBER_NOT_FOUND` | 404 | Номер не знайдено або він не належить акаунту. |
| `VALIDATION_ERROR` | 400 | Невалідне значення в тілі запиту. |

**curl**
```bash
curl -X PATCH https://app.piv.day/api/v1/numbers/vzPA1-kHKSg-EAL7e-Jqd3o \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "custom_name": "Support line" }'
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
    "phone_number": "+46764794425",
    "country_code": "SE",
    "status": "active",
    "custom_name": "Support line",
    "auto_renew": false,
    "expires_at": "2026-05-31T10:00:00Z",
    "tags": []
  }
}
```

### Керування авто-продовженням

`PATCH /api/v1/numbers/{piv_num_id}/auto-renewal`

Вмикає або вимикає авто-продовження номера. Коли увімкнено — продовження списується з балансу автоматично за 24 години до закінчення. Якщо коштів не вистачить, номер закінчиться; повернути його можна через `POST /numbers/restore` протягом 7 днів.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `auto_renew` * | boolean | Нове значення прапора авто-продовження. |

**curl**
```bash
curl -X PATCH https://app.piv.day/api/v1/numbers/vzPA1-kHKSg-EAL7e-Jqd3o/auto-renewal \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "auto_renew": true }'
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
    "phone_number": "+46764794425",
    "auto_renew": true
  }
}
```

### Відновити прострочені номери

`POST /api/v1/numbers/restore`

Повертає прострочені номери протягом 7-денного вікна. Передайте масив `piv_num_id` — номери повернуться зі збереженням SMS-історії та налаштувань. Вартість відновлення вища, ніж купівля нового номера (включено множник відновлення) — точні цифри видно в дашборді перед підтвердженням. Фінальний статус по кожному номеру прийде на вебхук `number.restore_completed`; за невдалі — гроші автоматично повертаються на баланс.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `piv_num_ids` * | string[] | Масив ID номерів для відновлення. |

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `NO_RESTORABLE_NUMBERS` | 404 | Жоден із переданих номерів не можна відновити (минув 7-денний термін або номер не ваш). |
| `INSUFFICIENT_BALANCE` | 402 | Недостатньо коштів для оплати відновлення. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/numbers/restore \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "piv_num_ids": [
      "vzPA1-kHKSg-EAL7e-Jqd3o",
      "abc12-defgh-ijklm-nopqr"
    ]
  }'
```

**200 OK**
```json
{
  "success": true,
  "queued": 2,
  "skipped": 0,
  "total_charged": 9.00,
  "numbers": [
    {
      "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
      "phone_number": "+46764794425",
      "status": "pending"
    }
  ]
}
```

### Історія SMS по номеру

`GET /api/v1/numbers/{piv_num_id}/sms`

Повертає вхідні та вихідні повідомлення по номеру в зворотному хронологічному порядку.

**Query parameters**

| Field | Type | Description |
| --- | --- | --- |
| `limit` | integer | Скільки повідомлень повернути (за замовчуванням 50, максимум 200). |
| `offset` | integer | Зміщення для пагінації. |

**curl**
```bash
curl "https://app.piv.day/api/v1/numbers/vzPA1-kHKSg-EAL7e-Jqd3o/sms?limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "messages": [
      {
        "id": "42",
        "from_number": "+46764794425",
        "to_number": "+14155551234",
        "message_body": "Hello from piv.day",
        "direction": "outbound",
        "status": "delivered",
        "received_at": "2026-05-22T11:30:00Z"
      },
      {
        "id": "41",
        "from_number": "+14155551234",
        "to_number": "+46764794425",
        "message_body": "Hey, got your message!",
        "direction": "inbound",
        "status": "received",
        "received_at": "2026-05-22T11:35:00Z"
      }
    ],
    "pagination": { "total": 2, "limit": 20, "offset": 0 }
  }
}
```

### Надіслати SMS

`POST /api/v1/numbers/{piv_num_id}/sms/send`

Доступно для країн, де дозволена вихідна відправка (наприклад CA, GB, SE). Вартість повідомлення списується з балансу в момент відправки.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `to_number` * | string | Номер отримувача в міжнародному форматі (E.164). |
| `message_body` * | string | Текст повідомлення. Підтримуються GSM-7 і UCS-2. |

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `NUMBER_NOT_FOUND` | 404 | Номер не знайдено або він не належить акаунту. |
| `NUMBER_EXPIRED` | 403 | Номер вже закінчився. |
| `NUMBER_NOT_ACTIVE` | 400 | Номер зараз у стані, що не дозволяє відправку. |
| `INSUFFICIENT_BALANCE` | 402 | Недостатньо коштів для відправки. |
| `INVALID_MESSAGE_FORMAT` | 400 | Тіло повідомлення перевищує допустиму довжину або містить заборонені символи. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/numbers/vzPA1-kHKSg-EAL7e-Jqd3o/sms/send \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "to_number": "+14155551234",
    "message_body": "Hello from piv.day"
  }'
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "message_id": "42",
    "from_number": "+46764794425",
    "to_number": "+14155551234",
    "message_body": "Hello from piv.day",
    "status": "queued",
    "created_at": "2026-05-22T11:30:00Z"
  }
}
```

### Запуск Google QR-верифікації

`POST /api/v1/numbers/{piv_num_id}/verify`

Запускає Google QR-верифікацію за наданим URL. Результат — успіх або помилка — прийде на вебхук verify.completed або verify.failed. Вартість списується з балансу в момент запуску.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `gv_url` * | string | Повний URL Google-верифікації (береться зі сторінки Google). |

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `NUMBER_NOT_FOUND` | 404 | Номер не знайдено або він не ваш. |
| `NUMBER_NOT_ACTIVE` | 400 | Номер не в активному статусі. |
| `SMS_DISABLED` | 400 | Відправка SMS вимкнена для цього номера. |
| `INSUFFICIENT_BALANCE` | 402 | Недостатньо коштів для верифікації. |
| `PROXY_DEAD` | 502 | Проксі недоступний — баланс повернено. |
| `QUEUE_FULL` | 503 | Сервер зайнятий, спробуйте пізніше — баланс повернено. |
| `SERVER_UNAVAILABLE` | 503 | Сервер автоматизації недоступний — баланс повернено. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/numbers/vzPA1-kHKSg-EAL7e-Jqd3o/verify \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "gv_url": "https://gv.google.com/..." }'
```

**202 Accepted**
```json
{
  "success": true,
  "message": "Verification initiated"
}
```

## Проксі

Резидентський IPv6 у десятках країн. Масова купівля, продовження, Restore з тим самим логіном і паролем, експорт у потрібному форматі.

### Список серверів

`GET /api/v1/proxy/servers`

Список доступних проксі-серверів з цінами.

**curl**
```bash
curl https://app.piv.day/api/v1/proxy/servers \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "servers": [
      {
        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "name": "EU-DE-01",
        "country": "DE",
        "socks5_port": 1080,
        "http_port": 3128,
        "price_per_proxy": 0.50
      },
      {
        "id": "b2c3d4e5-f6a7-8901-bcde-f12345678901",
        "name": "EU-NL-01",
        "country": "NL",
        "socks5_port": 1080,
        "http_port": 3128,
        "price_per_proxy": 0.45
      }
    ]
  }
}
```

### Список замовлень

`GET /api/v1/proxy/orders`

Список ваших замовлень проксі з можливістю фільтрації за статусом або пошуковим запитом.

**Query parameters**

| Field | Type | Description |
| --- | --- | --- |
| `limit` | integer | Кількість записів. За замовчуванням: 100, максимум: 500. |
| `offset` | integer | Зміщення для пагінації. |
| `status` | string | Фільтр за статусом: `active`, `expired`. |
| `search` | string | Пошук за замовленням. |

**curl**
```bash
curl "https://app.piv.day/api/v1/proxy/orders?status=active&limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "orders": [
      {
        "id": "ord-uuid-...",
        "country": "DE",
        "quantity": 10,
        "price_per_proxy": 0.50,
        "price_total": 5.00,
        "status": "active",
        "purchased_at": "2026-01-01T00:00:00Z",
        "expires_at": "2026-02-01T00:00:00Z"
      }
    ],
    "pagination": { "total": 1, "limit": 20, "offset": 0 }
  }
}
```

### Один замовлення

`GET /api/v1/proxy/orders/{id}`

Отримати деталі конкретного замовлення, включно зі списком проксі-облікових даних.

**curl**
```bash
curl https://app.piv.day/api/v1/proxy/orders/ord-uuid-... \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "order": {
      "id": "ord-uuid-...",
      "country": "DE",
      "quantity": 10,
      "price_per_proxy": 0.50,
      "price_total": 5.00,
      "status": "active",
      "purchased_at": "2026-01-01T00:00:00Z",
      "expires_at": "2026-02-01T00:00:00Z",
      "created_at": "2026-01-01T00:00:00Z",
      "items": [
        {
          "login": "user1",
          "password": "pass1",
          "host": "185.1.2.3",
          "socks5_port": 1080,
          "http_port": 3128,
          "ipv6": "2a00:1:2:3::1"
        }
      ]
    }
  }
}
```

### Купити проксі

`POST /api/v1/proxy/purchase`

Купити проксі на обраному сервері. Один запит створює одне замовлення з N проксі (до 500). Bulk-замовлень немає — за потреби викликайте метод повторно.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `server_id` * | string | ID сервера з `GET /proxy/servers`. |
| `quantity` * | integer | Скільки проксі купити (1–500). |
| `months` * | integer | Термін оренди в місяцях (1–12). |

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `INSUFFICIENT_BALANCE` | 402 | Недостатньо коштів. |
| `NOT_FOUND` | 404 | Проксі-сервер не знайдено або він неактивний. |
| `VALIDATION_ERROR` | 400 | Некоректне тіло запиту. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/proxy/purchase \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "server_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "quantity": 5,
    "months": 1
  }'
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "order": {
      "id": "ord-uuid-...",
      "country": "DE",
      "quantity": 5,
      "price_total": 2.50,
      "status": "active",
      "expires_at": "2026-02-01T00:00:00Z",
      "created_at": "2026-01-01T00:00:00Z",
      "items": [
        {
          "login": "user1",
          "password": "pass1",
          "host": "185.1.2.3",
          "socks5_port": 1080,
          "http_port": 3128,
          "ipv6": "2a00:1:2:3::1"
        },
        {
          "login": "user2",
          "password": "pass2",
          "host": "185.1.2.3",
          "socks5_port": 1080,
          "http_port": 3128,
          "ipv6": "2a00:1:2:3::2"
        }
      ]
    }
  }
}
```

### Продовжити замовлення

`POST /api/v1/proxy/orders/{id}/renew`

Продовжити активне замовлення проксі на додаткову кількість місяців.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `months` * | integer | Скільки місяців додати. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/proxy/orders/ord-uuid-.../renew \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "months": 1 }'
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "order_id": "ord-uuid-...",
    "cost": 5.00,
    "new_expires_at": "2026-03-01T00:00:00Z"
  }
}
```

### Restore прострочених замовлень

`POST /api/v1/proxy/orders/{id}/restore`

Відновити прострочений замовлення проксі без повторної купівлі. Логін, пароль, IPv6-адреси, країна і прив'язки — ті самі. Термін продовжується на 1 місяць. Антидетект переналаштовувати не потрібно.

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/proxy/orders/ord-uuid-.../restore \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "order_id": "ord-uuid-...",
    "quantity": 10,
    "total_cost": 5.00,
    "expires_at": "2026-03-01T00:00:00Z"
  }
}
```

### Експорт списку проксі

`GET /api/v1/proxy/orders/{id}/export`

Експортувати облікові дані проксі замовлення у вигляді текстового списку (login:password@host:port).

**Query parameters**

| Field | Type | Description |
| --- | --- | --- |
| `format` | string | Формат експорту: `socks5`, `http` або `both` (за замовчуванням `socks5`). |

**curl**
```bash
curl "https://app.piv.day/api/v1/proxy/orders/ord-uuid-.../export?format=socks5" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "content": "user1:pass1@1.2.3.4:1080\nuser2:pass2@1.2.3.4:1080",
    "filename": "piv-day-proxy-ord-uuid--socks5.txt",
    "format": "socks5",
    "count": 2
  }
}
```

## Домени

Пошук і реєстрація без KYC, автоматичний Cloudflare і SSL, управління DNS-записами та NS-серверами через API.

### Перевірка доступності

`GET /api/v1/domains/search`

Перевірити доступність і вартість реєстрації домену.

**Query parameters**

| Field | Type | Description |
| --- | --- | --- |
| `q` * | string | Ім'я домену для пошуку, наприклад `mysite.com`. |

**curl**
```bash
curl "https://app.piv.day/api/v1/domains/search?q=mysite.com" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "domain": "mysite.com",
    "available": true,
    "create_price": 12.50,
    "renew_price": 12.50,
    "currency": "USD"
  }
}
```

### Список доменів

`GET /api/v1/domains`

Список ваших зареєстрованих доменів з можливістю фільтрації.

**Query parameters**

| Field | Type | Description |
| --- | --- | --- |
| `limit` | integer | Кількість записів. За замовчуванням: 100, максимум: 500. |
| `offset` | integer | Зміщення для пагінації. |
| `status` | string | Статус домену: `active`, `pending`, `expired`. |
| `cloudflare` | string | Фільтр за статусом Cloudflare: `true` або `false`. |
| `auto_renew` | string | Фільтр за автопродовженням: `true` або `false`. |
| `search` | string | Пошук за доменним іменем. |

**curl**
```bash
curl "https://app.piv.day/api/v1/domains?status=active&limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "domains": [
      {
        "id": "dom-uuid-...",
        "domain_name": "mysite.com",
        "status": "active",
        "registered_at": "2026-01-01T00:00:00Z",
        "expires_at": "2027-01-01T00:00:00Z",
        "auto_renew": true,
        "cloudflare_enabled": true,
        "cloudflare_status": "active",
        "ssl_type": "lets_encrypt",
        "ssl_status": "active",
        "ssl_mode": "full",
        "ssl_expires_at": "2026-04-01T00:00:00Z",
        "created_at": "2026-01-01T00:00:00Z",
        "updated_at": "2026-01-01T00:00:00Z"
      }
    ],
    "pagination": { "total": 1, "limit": 20, "offset": 0 }
  }
}
```

### Реєстрація домену

`POST /api/v1/domains`

Зареєструвати новий домен. Запит асинхронний — повертає `queue_id` для відстеження. Cloudflare УВІМК: поле `nameservers` передавати не можна, опційно можна передати `dns_records` та `ssl_mode`. Cloudflare ВИМК: поля `dns_records` та `ssl_mode` передавати не можна, `nameservers` обов'язковий (не менше 2). NS-сервери Cloudflare ми ніколи не розкриваємо.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `domain` * | string | Ім'я домену для реєстрації, наприклад `mysite.com`. |
| `period` * | integer | Термін реєстрації в роках (1–10). |
| `cloudflare` * | boolean | Підключити домен до Cloudflare автоматично. |
| `auto_renew` * | boolean | Увімкнути автоматичне продовження. |
| `ssl_mode` | string | `flexible` \| `full` \| `strict`. Лише при `cloudflare: true`. |
| `nameservers` | string[] | Обов'язково при `cloudflare: false` (≥2 NS). Заборонено при `cloudflare: true`. |
| `dns_records` | object[] | Початкові DNS-записи. Лише при `cloudflare: true`. |

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `DOMAIN_NOT_AVAILABLE` | 400 | Домен недоступний для реєстрації. |
| `INSUFFICIENT_BALANCE` | 402 | Недостатньо коштів. |
| `VALIDATION_ERROR` | 400 | Некоректне тіло запиту. |

**Cloudflare УВІМК**
```bash
curl -X POST https://app.piv.day/api/v1/domains \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "mysite.com",
    "period": 1,
    "cloudflare": true,
    "auto_renew": true,
    "ssl_mode": "full",
    "dns_records": [
      { "type": "A", "name": "@", "content": "1.2.3.4", "proxied": true }
    ]
  }'
```

**Cloudflare ВИМК — власні NS**
```bash
curl -X POST https://app.piv.day/api/v1/domains \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": "mysite.com",
    "period": 1,
    "cloudflare": false,
    "auto_renew": false,
    "nameservers": ["ns1.myhost.com", "ns2.myhost.com"]
  }'
```

**202 Accepted**
```json
{
  "success": true,
  "data": {
    "queue_id": "que-uuid-...",
    "domain": {
      "id": "dom-uuid-...",
      "domain_name": "mysite.com",
      "status": "purchasing",
      "period": 1,
      "cloudflare_enabled": true,
      "auto_renew": true,
      "ssl_mode": "full",
      "created_at": "2026-01-01T00:00:00Z"
    }
  }
}
```

### Статус завдання реєстрації

`GET /api/v1/domains/queue/{id}`

Перевірити статус запису черги реєстрації або продовження домену. При статусі `completed` у відповіді одразу повна картка домену — окремий запит до `/domains/{id}` не потрібний.

**curl**
```bash
curl https://app.piv.day/api/v1/domains/queue/que-uuid-... \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**pending / processing**
```json
{
  "success": true,
  "data": {
    "queue_id": "que-uuid-...",
    "domain": "mysite.com",
    "status": "pending"
  }
}
```

**completed — повна картка**
```json
{
  "success": true,
  "data": {
    "queue_id": "que-uuid-...",
    "status": "completed",
    "domain": {
      "id": "dom-uuid-...",
      "domain_name": "mysite.com",
      "status": "active",
      "registered_at": "2026-01-01T12:00:00Z",
      "expires_at": "2027-01-01T12:00:00Z",
      "auto_renew": true,
      "cloudflare_enabled": true,
      "cloudflare_status": "active",
      "ssl_type": "lets_encrypt",
      "ssl_status": "active",
      "ssl_mode": "full",
      "ssl_expires_at": "2026-04-01T00:00:00Z",
      "created_at": "2026-01-01T00:00:00Z",
      "updated_at": "2026-01-01T12:00:00Z",
      "dns_records": [
        { "name": "@", "type": "A", "content": "1.2.3.4", "ttl": 1, "priority": null, "proxied": true }
      ]
    }
  }
}
```

### Один домен

`GET /api/v1/domains/{id}`

Отримати повну інформацію про зареєстрований домен. Поле `nameservers` присутнє лише якщо `cloudflare_enabled: false`.

**curl**
```bash
curl https://app.piv.day/api/v1/domains/dom-uuid-... \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "domain": {
      "id": "dom-uuid-...",
      "domain_name": "mysite.com",
      "status": "active",
      "registered_at": "2026-01-01T12:00:00Z",
      "expires_at": "2027-01-01T12:00:00Z",
      "auto_renew": true,
      "cloudflare_enabled": true,
      "cloudflare_status": "active",
      "ssl_type": "lets_encrypt",
      "ssl_status": "active",
      "ssl_mode": "full",
      "ssl_expires_at": "2026-04-01T00:00:00Z",
      "created_at": "2026-01-01T00:00:00Z",
      "updated_at": "2026-01-01T12:00:00Z",
      "dns_records": [
        { "name": "@", "type": "A", "content": "1.2.3.4", "ttl": 1, "priority": null, "proxied": true },
        { "name": "www", "type": "CNAME", "content": "mysite.com", "ttl": 1, "priority": null, "proxied": true }
      ]
    }
  }
}
```

### Продовжити домен

`POST /api/v1/domains/{id}/renew`

Продовжити реєстрацію домену. Повертає `queue_id` для відстеження.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `period` * | integer | На скільки років продовжити. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/domains/dom-uuid-.../renew \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "period": 1 }'
```

**202 Accepted**
```json
{
  "success": true,
  "data": {
    "queue_id": "que-uuid-...",
    "domain_id": "dom-uuid-...",
    "expires_at": "2027-01-01T12:00:00Z"
  }
}
```

### Авто-продовження

`POST /api/v1/domains/{id}/auto-renew`

Увімкнути або вимкнути автоматичне продовження домену.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `auto_renew` * | boolean | Нове значення прапора. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/domains/dom-uuid-.../auto-renew \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "auto_renew": true }'
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "id": "dom-uuid-...",
    "auto_renew": true
  }
}
```

### DNS-записи домену

`GET /api/v1/domains/{id}/dns`

Список DNS-записів домену (через Cloudflare).

**curl**
```bash
curl https://app.piv.day/api/v1/domains/dom-uuid-.../dns \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "records": [
      {
        "id": "rec-uuid-...",
        "type": "A",
        "name": "@",
        "content": "1.2.3.4",
        "ttl": 1,
        "priority": null,
        "proxied": true
      }
    ]
  }
}
```

### Додати DNS-запис

`POST /api/v1/domains/{id}/dns`

Створити новий DNS-запис.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `type` * | string | Тип запису: `A`, `AAAA`, `CNAME`, `MX`, `TXT` тощо. |
| `name` * | string | Ім'я запису, наприклад `@` або `subdomain`. |
| `content` * | string | Значення запису. |
| `ttl` | integer | TTL у секундах (`1` = авто). |
| `proxied` | boolean | Проксіювати через Cloudflare. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/domains/dom-uuid-.../dns \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "A",
    "name": "@",
    "content": "1.2.3.4",
    "ttl": 1,
    "proxied": true
  }'
```

**201 Created**
```json
{
  "success": true,
  "data": {
    "record": {
      "id": "rec-uuid-...",
      "type": "A",
      "name": "@",
      "content": "1.2.3.4",
      "ttl": 1,
      "priority": null,
      "proxied": true
    }
  }
}
```

### Змінити DNS-запис

`PUT /api/v1/domains/{id}/dns/{recordId}`

Оновити існуючий DNS-запис за ID. Перезаписуються лише передані поля.

**curl**
```bash
curl -X PUT https://app.piv.day/api/v1/domains/dom-uuid-.../dns/rec-uuid-... \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "content": "5.6.7.8" }'
```

### Видалити DNS-запис

`DELETE /api/v1/domains/{id}/dns/{recordId}`

Видалити DNS-запис за ID.

**curl**
```bash
curl -X DELETE https://app.piv.day/api/v1/domains/dom-uuid-.../dns/rec-uuid-... \
  -H "Authorization: Bearer YOUR_API_KEY"
```

### Увімкнути Cloudflare

`POST /api/v1/domains/{id}/cloudflare`

Увімкнути Cloudflare для домену. NS-сервери Cloudflare не повертаємо — делегування налаштовуємо зі свого боку. Вимкнення Cloudflare через цей ендпоінт не підтримується.

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/domains/dom-uuid-.../cloudflare \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "enabled": true }'
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "domain_id": "dom-uuid-...",
    "ssl_mode": "flexible"
  }
}
```

### Змінити NS-сервери

`POST /api/v1/domains/{id}/ns-servers`

Задати довільні NS-сервери для домену.

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/domains/dom-uuid-.../ns-servers \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "nameservers": ["ns1.example.com", "ns2.example.com"] }'
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "nameservers": [
      { "nameserver": "ns1.example.com", "order_index": 1 },
      { "nameserver": "ns2.example.com", "order_index": 2 }
    ]
  }
}
```

## Вайти

Генерація вайт-сторінок за нішею та тиром. Кожен сайт унікальний. Зміна домену та контактів — без перегенерації.

### Список тирів

`GET /api/v1/whites/tiers`

Список доступних тарифів генерації з цінами. `price` — ціна за генерацію вже для вашого акаунта (з урахуванням підписки), одне число. `quality: "premium"` коштує дорожче (обчислюється при створенні). Блог лише у `v2_tier3`: перші 3 статті включені, за кожну додаткову (до 20) — `extra_article_price`.

**curl**
```bash
curl https://app.piv.day/api/v1/whites/tiers \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "tiers": [
      {
        "tier_key": "v2_tier1",
        "name": "Landing",
        "generator_version": "v2",
        "min_articles": 0,
        "max_articles": 0,
        "price": 3.00,
        "extra_article_price": null
      },
      {
        "tier_key": "v2_tier2",
        "name": "Multi-page",
        "generator_version": "v2",
        "min_articles": 0,
        "max_articles": 0,
        "price": 6.00,
        "extra_article_price": null
      },
      {
        "tier_key": "v2_tier3",
        "name": "Full + Blog",
        "generator_version": "v2",
        "min_articles": 3,
        "max_articles": 20,
        "price": 10.00,
        "extra_article_price": 1.50
      }
    ]
  }
}
```

### Список вайтів

`GET /api/v1/whites`

Список ваших завдань генерації з можливістю фільтрації.

**Query parameters**

| Field | Type | Description |
| --- | --- | --- |
| `limit` | integer | Кількість записів. За замовчуванням: 100, максимум: 500. |
| `offset` | integer | Зміщення для пагінації. |
| `status` | string | `queued` \| `processing` \| `done` \| `failed`. |
| `tier_key` | string | `v2_tier1` \| `v2_tier2` \| `v2_tier3`. |

**curl**
```bash
curl "https://app.piv.day/api/v1/whites?status=done&limit=20" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "whites": [
      {
        "id": "job-uuid-...",
        "name": "My White",
        "status": 10,
        "tier_key": "v2_tier3",
        "quality": "standard",
        "niche": "IT Consulting",
        "country": "DE",
        "language": "de",
        "domain": "mysite.com",
        "result_url": null,
        "created_at": "2026-01-01T00:00:00Z"
      }
    ],
    "pagination": { "total": 1, "limit": 20, "offset": 0 }
  }
}
```

### Запустити генерацію

`POST /api/v1/whites`

Поставити завдання генерації вайт-сторінки в чергу. Оплата знімається відразу. Фінальний статус надходить на вебхук `white.completed` / `white.failed` або перевіряється через `GET /whites/{id}`.

**Request body**

| Field | Type | Description |
| --- | --- | --- |
| `name` * | string | Назва завдання (2–20 символів). |
| `tier_key` * | string | `v2_tier1` \| `v2_tier2` \| `v2_tier3`. |
| `niche` * | string | Тематика сайту (до 25 символів). |
| `country` * | string | Код країни (ISO 3166-1 alpha-2). |
| `language` * | string | Основна мова сайту (ISO 639-1). |
| `quality` | string | `standard` \| `premium` (за замовчуванням `standard`). |
| `languages` | string[] | Список мов. Перша включена в ціну, кожна наступна — доплата. |
| `domain` | string | Повний домен (напр. `example.com`). Без автопідстановки. |
| `email` | string | Повний email (напр. `info@example.com`). Без автопідстановки. |
| `phone` | string | Телефон контакту. |
| `address` | string | Адреса компанії. |
| `legal_name` | string | Юридична назва. |
| `style_hint` | string | Стиль дизайну (Random, Мінімалістичний, Корпоративний тощо). |
| `keywords` | string[] | Ключові слова для SEO. |
| `banned_words` | string[] | Слова, заборонені в контенті. |
| `blog_count` | integer | Кількість статей блогу 3–20. Тільки для `v2_tier3`. За статті понад 3 — доплата. |
| `contacts_mode` | string | `same` \| `template`. |
| `contacts_template` | string | Шаблон контактної сторінки (при `contacts_mode=template`). |
| `facebook` | string | Повний URL сторінки Facebook. |
| `instagram` | string | Повний URL профілю Instagram. |
| `linkedin` | string | Повний URL профілю LinkedIn. |
| `youtube` | string | Повний URL каналу YouTube. |
| `tiktok` | string | Повний URL профілю TikTok. |

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `INSUFFICIENT_BALANCE` | 402 | Недостатньо коштів. |
| `INVALID_TIER` | 400 | Тариф з таким ключем не знайдено. |
| `VALIDATION_ERROR` | 400 | Некоректне тіло запиту. |

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/whites \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Tech Blog DE",
    "tier_key": "v2_tier3",
    "niche": "IT Consulting",
    "country": "DE",
    "language": "de",
    "quality": "premium",
    "languages": ["de", "en"],
    "domain": "techblog-de.com",
    "email": "info@techblog-de.com",
    "phone": "+49 30 123456",
    "address": "Berliner Str. 1, 10115 Berlin",
    "legal_name": "TechBlog GmbH",
    "style_hint": "Корпоративный",
    "keywords": ["IT", "consulting", "cloud"],
    "banned_words": ["cheap", "free"],
    "blog_count": 8,
    "contacts_mode": "same",
    "facebook": "https://facebook.com/techblogde",
    "instagram": "https://instagram.com/techblogde",
    "linkedin": "https://linkedin.com/company/techblogde"
  }'
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "status": "queued",
    "white": {
      "id": "job-uuid-...",
      "name": "Tech Blog DE",
      "status": 0,
      "tier_key": "v2_tier3",
      "quality": "premium",
      "niche": "IT Consulting",
      "country": "DE",
      "language": "de",
      "domain": "techblog-de.com",
      "result_url": null,
      "created_at": "2026-01-01T00:00:00Z"
    }
  }
}
```

### Один вайт

`GET /api/v1/whites/{id}`

Отримати поточний стан завдання генерації.

**curl**
```bash
curl https://app.piv.day/api/v1/whites/job-uuid-... \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "white": {
      "id": "job-uuid-...",
      "name": "Tech Blog DE",
      "status": 10,
      "tier_key": "v2_tier3",
      "quality": "premium",
      "niche": "IT Consulting",
      "country": "DE",
      "language": "de",
      "domain": "techblog-de.com",
      "result_url": null,
      "created_at": "2026-01-01T00:00:00Z"
    }
  }
}
```

### Завантажити архів

`GET /api/v1/whites/{id}/download`

Отримати підписане короткоживуче посилання на готовий архів вайт-сторінки. Посилання діє ~5 хвилин (див. `expires_at`). Якщо минуло — просто запитай `/download` ще раз, видамо нове. Якщо вайт ще не згенерований, повернеться `409 NOT_READY`.

**Query parameters**

| Field | Type | Description |
| --- | --- | --- |
| `format` | string | Формат архіву. За замовчуванням `php`. |

**Errors**

| Code | HTTP | When it fires |
| --- | --- | --- |
| `NOT_READY` | 409 | Вайт ще генерується — повтори запит після `white.completed`. |

**curl**
```bash
curl "https://app.piv.day/api/v1/whites/job-uuid-.../download?format=php" \
  -H "Authorization: Bearer YOUR_API_KEY"
```

**200 OK**
```json
{
  "success": true,
  "data": {
    "url": "https://strg.piv.day/download/<user_id>/<job_id>?format=php&token=<hmac>&filename=<name>.zip",
    "filename": "example.com_2026-05-28_DE_en.zip",
    "format": "php",
    "expires_at": "2026-05-28T14:46:00Z"
  }
}
```

### Змінити домен та контакти

`POST /api/v1/whites/{id}/config`

Оновити контактні дані компанії у вайт-сторінці без повторної генерації.

**curl**
```bash
curl -X POST https://app.piv.day/api/v1/whites/job-uuid-.../config \
  -H "Authorization: Bearer YOUR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "company": "My Brand",
    "domain": "newdomain.com",
    "email": "info@newdomain.com",
    "phone": "+49 30 123456",
    "address": "Berliner Str. 1, Berlin",
    "legal": "My Brand GmbH"
  }'
```

## Webhooks

На ваш URL відправляється звичайний POST з JSON-тілом. Формат однаковий для всіх подій: `event_type`, `timestamp` і блок `data`. Ваш сервер повинен відповісти будь-яким 2xx протягом 5 секунд.

```
POST <your webhook URL>
Content-Type: application/json
User-Agent: piv.day-webhook/1.0

{
  "event_type": "<event identifier>",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": { ... }
}
```

**Retries.** При не-2xx або таймауті — до 3 повторних спроб із затримкою 1 с, 2 с, 4 с. Повна історія доставок доступна на сторінці API-ключів у дашборді. Якщо всі три не пройшли — подія позначається як failed, можна повторно надіслати вручну.

**Security.** Використовуйте HTTPS-ендпойнт. Кожен запит надходить із заголовком `User-Agent: piv.day-webhook/1.0`. URL для вебхуків налаштовується в профілі API-ключа.

### All events

| Event | When it fires |
| --- | --- |
| `sms.received` | На ваш номер надійшло SMS від довіреного відправника. |
| `sms.status_updated` | Вихідне SMS змінило статус — наприклад, перейшло у доставку або провалилося. |
| `number.restore_completed` | Пакетний Restore дійшов до кінця — надходить фінальний підсумок. |
| `verify.completed` | Верифікація Google-акаунта завершилася успішно. |
| `verify.failed` | Щось пішло не так — Google не пропустив. |
| `proxy.expires_soon` | До кінця оренди замовлення проксі залишилося близько трьох днів. |
| `domain.registered` | Замовлення на реєстрацію успішно закрилося — домен живий. |
| `domain.failed` | Замовлення не дійшло до кінця — часта причина: домен щойно зайняли. |
| `domain.expires_soon` | До закінчення домену залишилося близько тижня. |
| `white.completed` | Завдання генерації вайта завершилося успішно. |
| `white.failed` | Завдання впало — кошти повертаються на баланс. |

### Вхідне SMS на ваш номер

**`sms.received`**

Найчастіше це коди підтвердження від рекламних мереж. У payload надходить і сирий текст, і автоматично розпізнаний код (якщо є) — можна одразу передати у свій воркфлоу без парсингу. SMS від відомих спамних відправників через вебхук не надсилаються.

```json
{
  "event_type": "sms.received",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
    "text": "Your verification code is 123456",
    "code": "123456",
    "country": "SE",
    "received_at": "2026-05-22T12:34:56Z"
  }
}
```

### Статус надісланого SMS змінився

**`sms.status_updated`**

Підходить, якщо ви надсилаєте підтвердження від свого імені і хочете знати, що повідомлення реально дійшло. Якщо статус failed — у payload надходять код і текст помилки від оператора.

```json
{
  "event_type": "sms.status_updated",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
    "message_id": "42",
    "old_status": "sent",
    "new_status": "delivered",
    "error_code": null,
    "error_message": null
  }
}
```

### Відновлення номерів завершено

**`number.restore_completed`**

Містить два списки: які номери повернулися і які ні. За номери, що не повернулися, сума повертається на баланс — вона теж у payload. Зручно для скриптів: зрозуміло, що запитувати повторно.

```json
{
  "event_type": "number.restore_completed",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "restored": [
      {
        "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
        "phone_number": "+46764794425",
        "country": "SE"
      }
    ],
    "failed": [
      {
        "piv_num_id": "abc12-defgh-ijklm-nopqr",
        "phone_number": "+46123456789",
        "country": "SE",
        "refunded": 7.50
      }
    ],
    "total_restored": 1,
    "total_failed": 1,
    "total_refunded": 7.50
  }
}
```

### Google QR-верифікація пройшла успішно

**`verify.completed`**

Якщо в процесі прийшло SMS з кодом — воно вже тут, не потрібно окремо його забирати. Поля captured_phone і captured_message містять те, що надіслав Google.

```json
{
  "event_type": "verify.completed",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "task_id": 12,
    "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
    "status": "sms_sent",
    "captured_phone": "+14155551234",
    "captured_message": "Your Google verification code is 123456",
    "sms_message_id": 42,
    "sms_status": "queued"
  }
}
```

### Google QR-верифікація не пройшла

**`verify.failed`**

Поле error_code говорить, що саме: таймаут, відмова, невалідний URL. Сума за неуспішну верифікацію повертається на баланс автоматично.

```json
{
  "event_type": "verify.failed",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "task_id": 12,
    "piv_num_id": "vzPA1-kHKSg-EAL7e-Jqd3o",
    "status": "failed",
    "error_code": "VERIFY_TIMEOUT",
    "error_message": "Verification timed out"
  }
}
```

### Проксі незабаром закінчаться

**`proxy.expires_soon`**

Потрібно для тих, хто продовжує не автоматично: встигаєте заздалегідь побачити і вирішити — продовжити чи закрити кампанію. За бажання можна підписатися і відразу дергати POST /proxy/orders/{id}/renew.

```json
{
  "event_type": "proxy.expires_soon",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "order_id": "ord-uuid-...",
    "country": "DE",
    "quantity": 10,
    "expires_at": "2026-05-25T00:00:00Z"
  }
}
```

### Домен зареєстровано

**`domain.registered`**

Якщо при покупці ви запросили Cloudflare і SSL, всі вже підняті — прапор cloudflare_enabled покаже статус. Можна одразу додавати DNS-записи або розгортати сайт.

```json
{
  "event_type": "domain.registered",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "domain_id": "dom-uuid-...",
    "domain_name": "mysite.com",
    "registered_at": "2026-05-22T12:34:56Z",
    "expires_at": "2027-05-22T12:34:56Z",
    "cloudflare_enabled": true
  }
}
```

### Реєстрація домену не вдалася

**`domain.failed`**

Поле error містить зрозуміле пояснення. Сума повертається на баланс. Можна вибрати інше ім'я і оформити замовлення заново.

```json
{
  "event_type": "domain.failed",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "domain_id": "dom-uuid-...",
    "domain_name": "mysite.com",
    "error": "Domain is no longer available"
  }
}
```

### Домен незабаром закінчиться

**`domain.expires_soon`**

Зручно, якщо авто-продовження вимкнено: встигаєте продовжити вручну до того, як домен піде в редемпцію. Прапор auto_renew підкаже, чи потрібне втручання.

```json
{
  "event_type": "domain.expires_soon",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "domain_id": "dom-uuid-...",
    "domain_name": "mysite.com",
    "expires_at": "2026-05-29T00:00:00Z",
    "auto_renew": false
  }
}
```

### Вайт згенеровано

**`white.completed`**

Архів доступний через GET /whites/{id}/download. Якщо у вайта був вказаний домен, sitemap і контакти вже підставлені під нього — можна одразу деплоїти.

```json
{
  "event_type": "white.completed",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "job_id": "job-uuid-...",
    "name": "My White",
    "tier_key": "t1",
    "country": "DE"
  }
}
```

### Генерація вайта не вдалася

**`white.failed`**

Поле error пояснює причину. Можна одразу запустити нове завдання через POST /whites — без втрат.

```json
{
  "event_type": "white.failed",
  "timestamp": "2026-05-22T12:34:56Z",
  "data": {
    "job_id": "job-uuid-...",
    "name": "My White",
    "error": "Generation failed"
  }
}
```

## Error codes

Усі помилки повертаються в одному форматі: HTTP-статус + поле `error.code` + зрозуміле повідомлення в `error.message`.

```json
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Not enough balance: required $5.00, available $1.20"
  }
}
```

| Code | HTTP | When it fires |
| --- | --- | --- |
| `UNAUTHORIZED` | 401 | Ключ відсутній, прострочений або відкликаний. |
| `INSUFFICIENT_PERMISSIONS` | 403 | У ключа немає необхідного скоупу для цієї дії. |
| `PERMISSION_DENIED` | 403 | Доступ на рівні акаунту не дозволяє цю дію (наприклад, функцію вимкнено). |
| `VALIDATION_ERROR` | 400 | Невалідне тіло запиту. Повідомлення пояснює, яке поле і чому. |
| `NUMBER_NOT_FOUND` | 404 | Номер не існує або не належить акаунту. |
| `NUMBER_EXPIRED` | 403 | Номер прострочений — використайте Restore (протягом 7 днів) або придбайте новий. |
| `NUMBER_NOT_ACTIVE` | 400 | Номер перебуває в стані, що не дозволяє цю операцію. |
| `INSUFFICIENT_BALANCE` | 402 | Недостатньо коштів для виконання операції. |
| `INVALID_MESSAGE_FORMAT` | 400 | Тіло SMS занадто довге або містить заборонені символи. |
| `COUNTRY_NOT_AVAILABLE` | 400 | Країна недоступна для купівлі або продовження. |
| `NO_NUMBERS_AVAILABLE` | 400 | У вибраній країні зараз немає вільних номерів. |
| `NO_RESTORABLE_NUMBERS` | 404 | Жоден із переданих номерів більше не підлягає відновленню. |
| `RATE_LIMITED` | 429 | Перевищено rate limit. Зачекайте час, вказаний у заголовку `Retry-After`. |
| `INTERNAL_ERROR` | 500 | Щось пішло не так з нашого боку. Якщо повторюється — зверніться до служби підтримки. |
