📚 Документация Partner API

Обзор

Partner API позволяет интегрировать сервис аренды SMS-номеров в ваши приложения через модель Prepaid Wallet.

💰
Prepaid Wallet
Оптовое пополнение со скидкой 5-15%
Быстрый старт
Простая интеграция через REST API
🔒
Безопасность
HMAC-SHA256 подпись запросов
📱
SMS в реальном времени
Webhooks для мгновенных уведомлений

Константы системы

Типы аренды

  • SHORT - Краткосрочная аренда на 1 час
  • LONG - Долгосрочная аренда от 1 до 90 дней

Доступные страны

  • Spain - Испания Доступно
  • Germany - Германия Скоро
  • UK - Англия Скоро
⚠️ Важно: Типы аренды и коды стран являются константами. Используйте точные значения в запросах к API.

Аутентификация

Все API запросы должны быть подписаны с использованием HMAC-SHA256.

🌐 API Endpoint: https://telegram.smsbot.shop

Обязательные заголовки

HTTP Headers
X-Api-Key: pk_your_api_key_here
X-Signature: a3b9c1f2e4d6... (HMAC-SHA256 signature)
X-Timestamp: 1704106800000
Content-Type: application/json

Генерация подписи

JavaScript
const crypto = require('crypto');

function generateSignature(secretKey, timestamp, method, url, body) {
  // Формируем payload для подписи
  const payload = `${timestamp}${method}${url}${JSON.stringify(body || {})}`;

  // Создаем HMAC-SHA256 подпись
  return crypto
    .createHmac('sha256', secretKey)
    .update(payload)
    .digest('hex');
}

// Пример использования
const timestamp = Date.now().toString();
const signature = generateSignature(
  'sk_your_secret_key',
  timestamp,
  'POST',
  '/api/v1/wallet/topup',
  { amount: 100 }
);

console.log('Signature:', signature);

Пример для Postman Pre-request Script

Postman Pre-request
// CryptoJS is already available globally in Postman, no require needed.

function generateSignature(secretKey, timestamp, method, url, body) {
    const payload = `${timestamp}${method}${url}${JSON.stringify(body || {})}`;
    return CryptoJS.HmacSHA256(payload, secretKey).toString(CryptoJS.enc.Hex);
}

// Data extraction (use pm.request for everything)
const timestamp = Date.now().toString();
const method = pm.request.method;
const fullUrl = pm.request.url.toString(); // e.g., "https://api.example.com/api/v1/endpoint?query=param"
const domain = pm.environment.get("domain"); // Ensure this is "https://telegram.smsbot.shop" (full, including protocol)
const url = fullUrl.replace(domain, "").replace('{{domain}}', ''); // Results in "/api/v1/endpoint?query=param"
let body = {};

if (pm.request.body && pm.request.body.mode === 'raw' && pm.request.body.raw) {
    try {
        body = JSON.parse(pm.request.body.raw);
    } catch (e) {
        body = {};
    }
} // For non-raw bodies (e.g., form-data), it defaults to {} – adjust if needed for your API.

// Generate signature using env secretKey
const secretKey = pm.environment.get("secretKey");
const signature = generateSignature(secretKey, timestamp, method, url, body);

// Set env vars for headers
pm.environment.set("timestamp", timestamp);
pm.environment.set("signature", signature);

// Optional: Log for debugging (view in Postman Console)
console.log('Payload:', `${timestamp}${method}${url}${JSON.stringify(body || {})}`);
console.log('Signature:', signature);
💡 Настройка Postman: Создайте environment переменные domain и secretKey. Добавьте в Headers: X-Timestamp: {{timestamp}} и X-Signature: {{signature}}

Регистрация

Регистрация партнера

POST /api/v1/auth/register
JSON Request
{
  "name": "Название вашей компании",
  "webhookUrl": "https://your-domain.com/webhooks",
  "email": "admin@your-company.com",
  "phone": "+1234567890"
}
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": {
    "partnerId": "partner_xyz123",
    "apiKey": "pk_live_a1b2c3d4e5f6",
    "secretKey": "sk_live_9z8y7x6w5v4u",
    "status": "pending_approval",
    "message": "Аккаунт партнера создан. Ожидает одобрения."
  }
}
🔐 Важно: Сохраните ваш secretKey в безопасном месте. Его невозможно будет восстановить.

Проверка баланса

GET /api/v1/wallet/balance
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": {
    "balance": 1000.50,
    "balanceBonus": 50.00,
    "currency": "EUR",
    "total": 1050.50,
    "reserved": 25.00,
    "available": 1025.50
  }
}

Пополнение кошелька

POST /api/v1/wallet/topup

Доступные платежные системы

  • cryptomus - Криптовалютные платежи
  • nowpayments - Криптовалютные платежи
JSON Request
{
  "amount": 500,
  "provider": "cryptomus"
}
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": {
    "invoiceId": "inv_abc123xyz",
    "amount": 500,
    "finalAmount": 450,
    "discount": 50,
    "discountPercent": 10,
    "paymentUrl": "https://pay.cryptomus.com/invoice/abc123",
    "expiresAt": "2024-01-01T12:00:00Z",
    "status": "pending"
  }
}

История транзакций

GET /api/v1/wallet/transactions?page=1&limit=50
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": [
    {
      "id": "tx_abc123",
      "type": "topup",
      "amount": 100.00,
      "currency": "EUR",
      "balanceBefore": 900.00,
      "balanceAfter": 1000.00,
      "description": "Пополнение кошелька через Cryptomus",
      "metadata": {
        "provider": "cryptomus",
        "invoiceId": "inv_xyz789"
      },
      "createdAt": "2024-01-01T10:00:00Z"
    },
    {
      "id": "tx_def456",
      "type": "charge",
      "amount": -27.00,
      "currency": "EUR",
      "balanceBefore": 1000.00,
      "balanceAfter": 973.00,
      "description": "Number rental: +34612345678",
      "metadata": {
        "number": "+34612345678",
        "country": "Spain",
        "services": ["Google", "WhatsApp"],
        "period": "7",
        "rentType": "LONG"
      },
      "createdAt": "2024-01-01T11:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 150,
    "totalPages": 3
  }
}

Доступные сервисы

GET /api/v1/numbers/services?country=Spain&rentType=LONG

Параметры запроса

  • country - Код страны (Spain, Germany, UK)
  • rentType - Тип аренды (SHORT, LONG)
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": {
    "country": "Spain",
    "rentType": "LONG",
    "services": [
      {
        "id": "google",
        "name": "Google",
        "prices": {
          "1": 2.50,
          "3": 6.00,
          "7": 12.00,
          "30": 35.00
        },
        "currency": "€",
        "available": true,
        "priority": 1
      },
      {
        "id": "whatsapp",
        "name": "WhatsApp",
        "prices": {
          "1": 3.00,
          "7": 15.00,
          "30": 45.00
        },
        "currency": "€",
        "available": true,
        "priority": 2
      }
    ],
    "total": 2
  }
}

Расчет стоимости аренды

POST /api/v1/numbers/calculate-price
JSON Request
{
  "country": "Spain",
  "rentType": "LONG",
  "serviceIds": ["google", "whatsapp"]
}
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": {
    "country": "Spain",
    "rentType": "LONG",
    "services": [
      {"id": "google", "name": "Google"},
      {"id": "whatsapp", "name": "WhatsApp"}
    ],
    "periods": [
      {
        "period": "1",
        "duration": 1,
        "durationUnit": "days",
        "description": "1 day",
        "basePrice": 5.50,
        "price": 5.50,
        "currency": "€",
        "breakdown": [
          {"service": "Google", "price": 2.50},
          {"service": "WhatsApp", "price": 3.00}
        ]
      },
      {
        "period": "7",
        "duration": 7,
        "durationUnit": "days",
        "description": "7 days",
        "basePrice": 27.00,
        "price": 27.00,
        "currency": "€",
        "breakdown": [
          {"service": "Google", "price": 12.00},
          {"service": "WhatsApp", "price": 15.00}
        ]
      }
    ],
    "currency": "€"
  }
}

Аренда номера

POST /api/v1/numbers/rent
JSON Request
{
  "country": "Spain",
  "rentType": "LONG",
  "serviceIds": ["google", "whatsapp"],
  "period": "7"
}
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": {
    "rentId": "num_def456ghi",
    "number": "+34612345678",
    "country": "Spain",
    "services": [
      {"id": "google", "name": "Google"},
      {"id": "whatsapp", "name": "WhatsApp"}
    ],
    "price": 27.00,
    "currency": "€",
    "rentType": "LONG",
    "period": "7",
    "duration": {
      "value": 10080,
      "unit": "minutes"
    },
    "startsAt": "2024-01-01T10:00:00Z",
    "expiresAt": "2024-01-08T10:00:00Z",
    "status": "active"
  }
}

Активные номера

GET /api/v1/numbers/active?page=1&limit=50
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": [
    {
      "rentId": "num_abc123",
      "number": "+34612345678",
      "country": "Spain",
      "services": [
        {"id": "google", "name": "Google", "used": false},
        {"id": "whatsapp", "name": "WhatsApp", "used": true}
      ],
      "price": 27.00,
      "currency": "€",
      "rentType": "LONG",
      "period": "7",
      "status": "active",
      "startsAt": "2024-01-01T10:00:00Z",
      "expiresAt": "2024-01-08T10:00:00Z",
      "rentedAt": "2024-01-01T10:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 10,
    "totalPages": 1
  }
}

Продление аренды

Расчет стоимости продления

GET /api/v1/numbers/{rentId}/extend/calculate
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": {
    "rentId": "num_abc123",
    "number": "+34612345678",
    "currentExpiry": "2024-01-08T10:00:00Z",
    "availablePeriods": [
      {
        "period": "1",
        "duration": 1,
        "durationUnit": "days",
        "description": "1 day",
        "price": 5.50,
        "currency": "€"
      },
      {
        "period": "7",
        "duration": 7,
        "durationUnit": "days",
        "description": "7 days",
        "price": 27.00,
        "currency": "€"
      }
    ],
    "currency": "€"
  }
}

Продлить аренду

POST /api/v1/numbers/{rentId}/extend
JSON Request
{
  "period": "7"
}
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": {
    "rentId": "num_abc123",
    "number": "+34612345678",
    "newExpiresAt": "2024-01-15T10:00:00Z",
    "extensionPrice": 27.00,
    "currency": "€",
    "period": "7"
  }
}

Переаренда номера

Расчет стоимости переаренды

GET /api/v1/numbers/{rentId}/re-rent/calculate
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": {
    "rentId": "num_abc123",
    "number": "+34612345678",
    "status": "inactive",
    "lastExpiry": "2024-01-08T10:00:00Z",
    "services": [
      {"id": "google", "name": "Google"},
      {"id": "whatsapp", "name": "WhatsApp"}
    ],
    "availablePeriods": [
      {
        "period": "1",
        "duration": 1,
        "durationUnit": "days",
        "description": "1 day",
        "price": 5.50,
        "currency": "€"
      },
      {
        "period": "7",
        "duration": 7,
        "durationUnit": "days",
        "description": "7 days",
        "price": 27.00,
        "currency": "€"
      }
    ],
    "currency": "€"
  }
}

Переарендовать номер

POST /api/v1/numbers/{rentId}/re-rent
JSON Request
{
  "period": "7"
}
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": {
    "rentId": "num_abc123",
    "number": "+34612345678",
    "period": "7",
    "price": 27.00,
    "currency": "€",
    "startsAt": "2024-01-10T10:00:00Z",
    "expiresAt": "2024-01-17T10:00:00Z",
    "status": "active"
  }
}
💡 Переаренда: Позволяет переарендовать номера из истории (статус inactive). Полезно для повторного использования номеров.

Отмена аренды

POST /api/v1/numbers/{rentId}/cancel
✅ Успешный ответ
JSON Response
{
  "success": true,
  "message": "Rental cancelled and refunded",
  "data": {
    "rentId": "num_abc123",
    "number": "+34612345678",
    "status": "INACTIVE",
    "refunded": true,
    "refundAmount": 27.00,
    "currency": "€"
  }
}
💰 Возврат средств: Деньги возвращаются автоматически, если на номер не приходили SMS сообщения.

История SMS

GET /api/v1/numbers/{rentId}/sms?page=1&limit=50
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": [
    {
      "id": "sms_xyz789",
      "from": "Google",
      "message": "Your verification code is 123456",
      "receivedAt": "2024-01-01T12:00:00Z"
    },
    {
      "id": "sms_abc456",
      "from": "WhatsApp",
      "message": "WhatsApp code: 789012",
      "receivedAt": "2024-01-01T11:30:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 5,
    "totalPages": 1
  }
}

История транзакций

GET /api/v1/wallet/transactions?page=1&limit=50
✅ Успешный ответ
JSON Response
{
  "success": true,
  "data": [
    {
      "id": "tx_abc123",
      "type": "topup",
      "amount": 100.00,
      "currency": "EUR",
      "balanceBefore": 900.00,
      "balanceAfter": 1000.00,
      "description": "Пополнение кошелька через Cryptomus",
      "metadata": {
        "provider": "cryptomus",
        "invoiceId": "inv_xyz789"
      },
      "createdAt": "2024-01-01T10:00:00Z"
    },
    {
      "id": "tx_def456",
      "type": "charge",
      "amount": -27.00,
      "currency": "EUR",
      "balanceBefore": 1000.00,
      "balanceAfter": 973.00,
      "description": "Number rental: +34612345678",
      "metadata": {
        "number": "+34612345678",
        "country": "Spain",
        "services": ["Google", "WhatsApp"],
        "period": "7",
        "rentType": "LONG"
      },
      "createdAt": "2024-01-01T11:00:00Z"
    }
  ],
  "pagination": {
    "page": 1,
    "limit": 50,
    "total": 150,
    "totalPages": 3
  }
}

Webhooks

Мы отправляем уведомления в реальном времени на ваш webhook URL при важных событиях.

События Webhook

  • sms.received - Получено новое SMS
  • number.expired - Истек срок аренды номера
  • balance.low - Баланс ниже порогового значения
  • payment.completed - Платеж за пополнение завершен
  • payment.failed - Платеж за пополнение не удался

Формат Webhook

JSON Webhook Payload
{
  "event": "sms.received",
  "data": {
    "numberId": "num_def456ghi",
    "number": "+34612345678",
    "from": "Google",
    "message": "Your verification code is: 123456",
    "receivedAt": "2024-01-01T10:00:00Z"
  },
  "webhookId": "wh_789xyz",
  "timestamp": "1704106800000",
  "signature": "a1b2c3d4e5f6..."
}

Проверка подписи Webhook

Node.js / Express
const crypto = require('crypto');
const express = require('express');
const app = express();

// Middleware для парсинга JSON
app.use(express.json());

function verifyWebhookSignature(secretKey, signature, body) {
  // Создаем строку из тела запроса
  const payload = JSON.stringify(body);

  // Генерируем ожидаемую подпись
  const expectedSignature = crypto
    .createHmac('sha256', secretKey)
    .update(payload)
    .digest('hex');

  // Сравниваем подписи (защита от timing attacks)
  return crypto.timingSafeEqual(
    Buffer.from(signature),
    Buffer.from(expectedSignature)
  );
}

// Endpoint для приема webhook
app.post('/webhooks', (req, res) => {
  const signature = req.headers['x-webhook-signature'];
  const timestamp = req.headers['x-webhook-timestamp'];

  // Проверяем подпись
  if (!verifyWebhookSignature(process.env.SECRET_KEY, signature, req.body)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  // Проверяем timestamp (защита от replay attacks)
  const currentTime = Date.now();
  const webhookTime = parseInt(timestamp);
  const timeDiff = Math.abs(currentTime - webhookTime);

  if (timeDiff > 300000) { // 5 минут
    return res.status(401).json({ error: 'Request too old' });
  }

  // Обрабатываем различные события
  switch (req.body.event) {
    case 'sms.received':
      console.log('SMS received:', req.body.data);
      // Ваша логика обработки SMS
      break;

    case 'number.expired':
      console.log('Number expired:', req.body.data);
      // Логика для истекших номеров
      break;

    case 'balance.low':
      console.log('Low balance alert:', req.body.data);
      // Уведомление о низком балансе
      break;

    default:
      console.log('Unknown event:', req.body.event);
  }

  // Отправляем подтверждение
  res.status(200).json({ received: true });
});

// Запуск сервера
app.listen(3000, () => {
  console.log('Webhook server running on port 3000');
});

Лимиты запросов

  • По умолчанию: 100 запросов за 15 минут с одного IP
  • Авторизованные: 1000 запросов за 15 минут на партнера
  • Повтор webhook: Максимум 3 попытки с экспоненциальной задержкой

Коды ошибок

Код Описание Решение
400 Bad Request - Некорректные параметры Проверьте формат и обязательные поля запроса
401 Unauthorized - Неверный API ключ или подпись Проверьте API ключ и правильность генерации подписи
403 Forbidden - Аккаунт заблокирован Свяжитесь с поддержкой для разблокировки
404 Not Found - Ресурс не найден Проверьте правильность endpoint и ID ресурса
429 Too Many Requests - Превышен лимит Подождите перед следующим запросом
500 Internal Server Error Попробуйте позже или обратитесь в поддержку

Формат ответа с ошибкой

JSON Error Response
{
  "success": false,
  "error": {
    "code": "INSUFFICIENT_BALANCE",
    "message": "Недостаточно средств на балансе",
    "details": {
      "required": 50.00,
      "available": 25.50
    }
  },
  "requestId": "req_abc123xyz"
}

Финансовая модель

💼 Модель Prepaid Wallet

  • Создается корпоративный кошелек в системе
  • Пополнение оптом со скидкой (5-15%)
  • Платежное решение на вашей стороне
  • Устанавливаете любые цены для клиентов
  • Автоматическое списание по базовым ценам
  • Минимальное пополнение: 100€ (для тестов пополнение через Cryptomus от 5€)

Пример расчета

Параметр Значение
Пополнение 1000€
Скидка 10%
Получаете на баланс 1111€ кредитов
Ваша выгода 111€

Пример интеграции с полным циклом

JavaScript - Full Example
// Полный пример интеграции Partner API
const crypto = require('crypto');
const axios = require('axios');

class PartnerAPIClient {
  constructor(apiKey, secretKey) {
    this.apiKey = apiKey;
    this.secretKey = secretKey;
    this.baseURL = 'https://telegram.smsbot.shop/api/v1';
  }

  // Генерация подписи для запроса
  generateSignature(method, path, body = null) {
    const timestamp = Date.now().toString();
    const payload = `${timestamp}${method}${path}${JSON.stringify(body || {})}`;

    const signature = crypto
      .createHmac('sha256', this.secretKey)
      .update(payload)
      .digest('hex');

    return { signature, timestamp };
  }

  // Выполнение API запроса
  async request(method, path, data = null) {
    const { signature, timestamp } = this.generateSignature(method, path, data);

    try {
      const response = await axios({
        method,
        url: `${this.baseURL}${path}`,
        headers: {
          'X-Api-Key': this.apiKey,
          'X-Signature': signature,
          'X-Timestamp': timestamp,
          'Content-Type': 'application/json'
        },
        data
      });

      return response.data;
    } catch (error) {
      if (error.response) {
        throw new Error(`API Error: ${error.response.data.error}`);
      }
      throw error;
    }
  }

  // Проверка баланса
  async getBalance() {
    return this.request('GET', '/wallet/balance');
  }

  // Пополнение кошелька
  async topupWallet(amount, provider = 'cryptomus') {
    return this.request('POST', '/wallet/topup', { amount, provider });
  }

  // Аренда номера
  async rentNumber(params) {
    return this.request('POST', '/numbers/rent', params);
  }

  // Получение активных номеров
  async getActiveNumbers() {
    return this.request('GET', '/numbers/active');
  }

  // Получение истории SMS
  async getSMSHistory(numberId) {
    return this.request('GET', `/numbers/${numberId}/sms`);
  }
}

// Использование клиента
async function main() {
  const client = new PartnerAPIClient(
    'pk_live_your_api_key',
    'sk_live_your_secret_key'
  );

  try {
    // 1. Проверяем баланс
    const balance = await client.getBalance();
    console.log('Current balance:', balance.data.total);

    // 2. Если баланс низкий, пополняем
    if (balance.data.total < 100) {
      const topup = await client.topupWallet(500);
      console.log('Payment URL:', topup.data.paymentUrl);
    }

    // 3. Арендуем номер
    const rental = await client.rentNumber({
      country: 'Spain',
      services: ['google', 'whatsapp'],
      type: 'LONG',
      duration: 24
    });

    console.log('Rented number:', rental.data.number);
    console.log('Expires at:', rental.data.expiresAt);

    // 4. Проверяем SMS (polling)
    const checkSMS = async () => {
      const sms = await client.getSMSHistory(rental.data.numberId);

      if (sms.data.messages.length > 0) {
        console.log('New SMS:', sms.data.messages);
        return true;
      }

      return false;
    };

    // Polling каждые 5 секунд
    const interval = setInterval(async () => {
      const hasNewSMS = await checkSMS();
      if (hasNewSMS) {
        clearInterval(interval);
      }
    }, 5000);

  } catch (error) {
    console.error('Error:', error.message);
  }
}

// Запуск
main();