Аутентификация¶
Каждый запрос аутентифицируется подписанным API-ключом (HMAC-SHA256). Администратор вашей организации создаёт ключ в админ-панели и передаёт его вашему бэкенду, который подписывает каждый запрос.
Обзор¶
Каждый запрос должен содержать три заголовка, вычисленных из вашего Key ID и Secret:
| Заголовок | Описание |
|---|---|
X-API-Key |
Идентификатор ключа (публичный) |
X-Timestamp |
Текущее Unix-время в секундах |
X-Signature |
HMAC-SHA256 от канонической строки запроса |
Получение учётных данных¶
Администратор организации создаёт ключ в админ-панели (Settings → API Keys → Create API Key) и передаёт вам Key ID и Secret. Секрет показывается только один раз при создании.
Поля в ответе:
| Поле | Описание |
|---|---|
keyId |
Публичный идентификатор — используйте как заголовок X-API-Key |
secret |
Секрет для подписи HMAC — никогда не публикуйте |
Храните секрет надёжно
Секрет показывается только один раз при создании. Сохраните его в менеджере секретов, переменной окружения или защищённом хранилище. Позже его восстановить нельзя.
Подпись запроса¶
Шаг 1: Каноническая строка¶
Каноническая строка состоит из четырёх компонентов, разделённых переводом строки:
| Компонент | Описание | Пример |
|---|---|---|
timestamp |
Unix-время в секундах (то же, что в X-Timestamp) |
1708600000 |
METHOD |
HTTP-метод заглавными буквами | POST |
path |
Путь запроса с ведущим слешем, без хоста | /vaults |
bodyHash |
SHA-256 от тела запроса в hex (для пустого тела — хеш пустой строки) | e3b0c44298fc1c14... |
Шаг 2: HMAC-подпись¶
Подпишите каноническую строку секретом по алгоритму HMAC-SHA256, затем закодируйте результат в hex-строку нижнего регистра.
Шаг 3: Отправка запроса¶
Передайте все три заголовка:
Окно по времени
Сервер отклоняет запросы со временем старше 30 секунд. Убедитесь, что часы вашего сервера синхронизированы (NTP).
Примеры кода¶
Python¶
import hashlib
import hmac
import time
import requests
API_KEY_ID = "your-key-id"
API_SECRET = "your-secret"
BASE_URL = "https://api.example.com"
def sign_request(method: str, path: str, body: str = "") -> dict:
timestamp = str(int(time.time()))
body_hash = hashlib.sha256(body.encode()).hexdigest()
canonical = f"{timestamp}\n{method}\n{path}\n{body_hash}"
signature = hmac.new(
API_SECRET.encode(), canonical.encode(), hashlib.sha256
).hexdigest()
return {
"X-API-Key": API_KEY_ID,
"X-Timestamp": timestamp,
"X-Signature": signature,
}
# GET-запрос
path = "/vaults"
headers = sign_request("GET", path)
response = requests.get(f"{BASE_URL.rstrip('/')}{path}", headers=headers)
# POST-запрос
path = "/vaults"
body = '{"externalId":"cust_123","name":"Alice"}'
headers = sign_request("POST", path, body)
headers["Content-Type"] = "application/json"
response = requests.post(f"{BASE_URL.rstrip('/')}{path}", headers=headers, data=body)
Node.js¶
const crypto = require("crypto");
const API_KEY_ID = "your-key-id";
const API_SECRET = "your-secret";
function signRequest(method, path, body = "") {
const timestamp = Math.floor(Date.now() / 1000).toString();
const bodyHash = crypto.createHash("sha256").update(body).digest("hex");
const canonical = `${timestamp}\n${method}\n${path}\n${bodyHash}`;
const signature = crypto
.createHmac("sha256", API_SECRET)
.update(canonical)
.digest("hex");
return {
"X-API-Key": API_KEY_ID,
"X-Timestamp": timestamp,
"X-Signature": signature,
};
}
// GET
const headers = signRequest("GET", "/vaults");
fetch("https://api.example.com/vaults", { headers });
// POST
const body = JSON.stringify({ externalId: "cust_123", name: "Alice" });
const postHeaders = signRequest("POST", "/vaults", body);
postHeaders["Content-Type"] = "application/json";
fetch("https://api.example.com/vaults", {
method: "POST",
headers: postHeaders,
body,
});
C¶
using System.Security.Cryptography;
using System.Text;
var apiKeyId = "your-key-id";
var apiSecret = "your-secret";
string SignRequest(string method, string path, string body = "")
{
var timestamp = DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
var bodyHash = Convert.ToHexStringLower(
SHA256.HashData(Encoding.UTF8.GetBytes(body)));
var canonical = $"{timestamp}\n{method}\n{path}\n{bodyHash}";
var signature = Convert.ToHexStringLower(
HMACSHA256.HashData(
Encoding.UTF8.GetBytes(apiSecret),
Encoding.UTF8.GetBytes(canonical)));
return signature; // Заголовки X-API-Key, X-Timestamp, X-Signature заполните самостоятельно
}
cURL (Bash)¶
API_KEY_ID="your-key-id"
API_SECRET="your-secret"
TIMESTAMP=$(date +%s)
METHOD="GET"
PATH_URL="/vaults"
BODY=""
BODY_HASH=$(echo -n "$BODY" | openssl dgst -sha256 -hex | awk '{print $NF}')
CANONICAL="${TIMESTAMP}\n${METHOD}\n${PATH_URL}\n${BODY_HASH}"
SIGNATURE=$(echo -ne "$CANONICAL" | openssl dgst -sha256 -hmac "$API_SECRET" -hex | awk '{print $NF}')
curl "https://api.example.com${PATH_URL}" \
-H "X-API-Key: $API_KEY_ID" \
-H "X-Timestamp: $TIMESTAMP" \
-H "X-Signature: $SIGNATURE"
Список разрешённых IP¶
API-ключ можно ограничить определёнными IP-адресами. При настроенном списке
запросы с других адресов получают 401 Unauthorized.
Список настраивается в админ-панели: Settings → API Keys → Edit.
Ответы об ошибках¶
| Статус | Что значит |
|---|---|
401 Unauthorized |
Ключ отсутствует, недействителен или просрочен. Подпись не совпадает. IP не разрешён. Расхождение по времени > 30 с. Повтор — та же тройка (keyId, timestamp, signature) уже принята в окне сдвига; пересчитайте подпись с новым timestamp. |
403 Forbidden |
Учётные данные верны, но скоупа недостаточно для запрошенной операции. |
429 Too Many Requests |
Превышен лимит (по умолчанию 120 запросов/мин/ключ). Уважайте Retry-After. |
За пределами аутентификации¶
Три рантайм-поведения входят в контракт партнёра — прочитайте до релиза:
Idempotency-Keyобязателен наPOST /transactions/withdrawиPOST /transactions/transfer. Генерируйте UUID на каждую логическую операцию; повторы возвращают исходный ответ.- Защита от повторов. Каждая принятая подпись одноразова в пределах 30-секундного окна — повтор ОБЯЗАН пересчитать подпись.
- Rate limit. 120 авторизованных запросов / мин / ключ, sliding
window. В
429естьRetry-After.
Полный справочник: Надёжность и лимиты.
Дальше¶
- Быстрый старт — первые подписанные вызовы API.
- Справочник API — полный интерактивный справочник.