Меню документации

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

Каждый аутентифицированный запрос подписывается секретом вашего API-ключа. Сам секрет никогда не передается по сети: сервер заново вычисляет подпись из тех же байтов запроса и сверяет результат.

Перед началом работы

Тип учетных данныхФорматНазначениеСрок действия
API-ключ + секретak_live_… / sk_live_… (в тестнете Nile: ak_test_… / sk_test_…)Каждый вызов с вашего бэкендаБессрочно, пока не отозван или не наступил expires_at
Bootstrap tokenAuthorization: Bearer abt_…Создание аккаунта, адрес для депозита при регистрации, GET /v1/account и выпуск первого ключа — больше ничего15 минут
Без учетных данных (публичные)—GET /v1/prices, GET /v1/estimate, GET /v1/orderbook, GET /v1/resources/{address} и получение челленджаОграничено лимитами запросов на IP-адрес

API-ключ предназначен исключительно для машинного взаимодействия: никогда не используйте его в коде веб-страниц. Ключ можно создать до внесения первого депозита, чтобы ваш сервис мог получить адрес пополнения (GET /v1/deposit-addresses) и пополнить баланс самостоятельно; до поступления средств заказы будут отклоняться с ошибкой 4001 insufficient_funds. Секрет ключа отображается ровно один раз при создании — сохраните его в надежном месте.

Три заголовка запроса

ЗаголовокЗначение
X-API-KEYИдентификатор ключа, как указано в кабинете. Не является секретом.
X-API-TIMESTAMPТекущее время UTC в формате ISO 8601 с миллисекундами, например 2026-09-11T18:04:05.123Z.
X-API-SIGNbase64(HMAC_SHA256(api_secret, canonical_string))

Каноническая строка (Canonical string)

text
canonical_string = timestamp + METHOD + path + query + body
Часть строкиТребования
timestampЗначение заголовка X-API-TIMESTAMP, байт в байт.
METHODHTTP-метод заглавными буквами: GET, POST, PATCH, DELETE.
pathПуть запроса с префиксом /v1, закодированный точно так же, как отправляется по сети, например /v1/orders.
queryПустая строка "", если параметров нет; при наличии параметров — символ ? и строка параметров в исходном виде (без пересортировки и повторного кодирования).
bodyСырое тело запроса в кодировке UTF-8; пустая строка "", если тела нет (например, при запросах GET).

Никаких разделителей между частями нет. Сериализуйте тело запроса один раз и отправляйте именно эти байты: подписание форматированного JSON с отступами и отправка компактного JSON — самая частая причина ошибки 1002 invalid_signature.

Шаг 1 — Сформируйте и подпишите строку

Пример подписания запроса GET /v1/balance с секретом sk_test_example во время 2026-09-11T18:04:05.123Z:

text
2026-09-11T18:04:05.123ZGET/v1/balance
TS=2026-09-11T18:04:05.123Z
printf '%s' "${TS}GET/v1/balance" | openssl dgst -sha256 -hmac "sk_test_example" -binary | base64
import { createHmac } from "node:crypto";

const ts = "2026-09-11T18:04:05.123Z";
const sign = createHmac("sha256", "sk_test_example").update(`${ts}GET/v1/balance`).digest("base64");
console.log(sign);
import base64, hashlib, hmac

ts = "2026-09-11T18:04:05.123Z"
digest = hmac.new(b"sk_test_example", f"{ts}GET/v1/balance".encode(), hashlib.sha256).digest()
print(base64.b64encode(digest).decode())

Во всех трех случаях вывод будет идентичен: zqttXJ133TRJ7aj14dJ3jamfeCG2mDj+TbNPrrBJl2g=. Сверьтесь с этим значением перед отладкой других параметров.

Шаг 2 — Отправьте запрос

TS=$(date -u +%Y-%m-%dT%H:%M:%S.000Z)
SIGN=$(printf '%s' "${TS}GET/v1/balance" | openssl dgst -sha256 -hmac "$TENERGY_SECRET" -binary | base64)
curl -s https://api-nile.tenergy.me/v1/balance \
  -H "X-API-KEY: $TENERGY_KEY" -H "X-API-TIMESTAMP: $TS" -H "X-API-SIGN: $SIGN"
import { createHmac } from "node:crypto";

const ts = new Date().toISOString();
const sign = createHmac("sha256", process.env.TENERGY_SECRET!)
  .update(`${ts}GET/v1/balance`)
  .digest("base64");
const res = await fetch("https://api-nile.tenergy.me/v1/balance", {
  headers: { "X-API-KEY": process.env.TENERGY_KEY!, "X-API-TIMESTAMP": ts, "X-API-SIGN": sign },
});
console.log(res.status, await res.json());
import base64, hashlib, hmac, json, os, urllib.error, urllib.request
from datetime import datetime, timezone

ts = datetime.now(timezone.utc).isoformat(timespec="milliseconds").replace("+00:00", "Z")
sign = base64.b64encode(hmac.new(os.environ["TENERGY_SECRET"].encode(),
                                 f"{ts}GET/v1/balance".encode(), hashlib.sha256).digest()).decode()
req = urllib.request.Request("https://api-nile.tenergy.me/v1/balance", headers={
    "X-API-KEY": os.environ["TENERGY_KEY"], "X-API-TIMESTAMP": ts, "X-API-SIGN": sign})
try:
    with urllib.request.urlopen(req) as res:
        print(res.status, json.load(res))
except urllib.error.HTTPError as err:
    print(err.code, json.load(err))

В руководстве быстрого старта эта логика обернута в удобную функцию-хелпер tenergy(method, path, body).

Смещение часов, защита от повторов и правила IP

ПравилоДопустимое значениеОшибка при нарушении
Смещение часов (Clock skew)±5 секунд от серверного времени1003 signature_timestamp_skew — поле details.server_time содержит точное время сервера
Защита от повторов (Replay)Подпись действует ровно один раз в рамках окна1009 replayed_signature — для каждого запроса (включая повторные попытки) создавайте новое время и подпись
Белый список IPОпционально для ключа; если пуст — разрешены любые адреса1004 ip_not_allowed — поле details.source_ip содержит зафиксированный IP-адрес
ОкружениеКлючи ak_live_ — только для основного хоста, ak_test_ — только для Nile1012 key_environment_mismatch, отклоняется еще до поиска ключа в базе

Обязательно настройте службу синхронизации времени NTP на всех серверах, генерирующих подпись. Рассинхронизация часов приводит к ошибке 1003 на каждом запросе; увеличение количества повторов здесь не поможет.

Области видимости ключей (Scopes)

API-ключ получает только те разрешения, которые были явно указаны при его создании: поле scopes обязательно, и система не имеет предустановленных наборов разрешений. Названия прав соответствуют формату area.action (тип ApiKeyScope в openapi.yaml); в справочнике по API-ключам подробно описано назначение каждого скоупа. Вызов операции вне прав ключа вернет статус 403 с кодом 1005 insufficient_scope и указанием недостающего скоупа в поле details.required_scope. Запрашивайте только минимально необходимые для интеграции права.

Идемпотентность

ЗапросКлюч идемпотентностиПоведение при повторном вызове
POST /v1/ordersПоле client_order_id в теле запросаВозвращает исходный заказ, статус 200, без повторного списания средств
Другие изменяющие вызовыЗаголовок Idempotency-Key, от 8 до 128 символовВозвращает исходный результат выполнения

История идемпотентности хранится 24 часа. Отправка того же ключа с другим телом запроса отклоняется с ошибкой 3010 idempotency_conflict.

Следующие шаги

  • Быстрый стартпрактическое использование функции подписания: от расчета стоимости до подтверждения заказа.
  • Коды ошибок и лимитыкоды серии 1xxx и рекомендации по их устранению.
  • Окружениясоответствие хостов и типов API-ключей.

    ↑ ↓ — выбрать · Enter — открыть · Esc — закрыть