Ключ
Ключ выдаётся в кабинете, в разделе «Интеграции». Он показывается один раз: в базе лежит только его хеш, и повторить показ невозможно даже администратору. Потеряли — отзовите и выпустите новый.
Ключ передаётся заголовком. В строке запроса он не принимается намеренно: адрес попадает в журналы прокси, в историю браузера и в чужие Referer.
curl -s https://123-opt.com/api/v1/me \
-H "Authorization: Bearer sk_live_ВАШ_КЛЮЧ"
У каждого ключа свой потолок запросов в час. При его превышении приходит ответ 429 с заголовком Retry-After.
Что можно получить
# Свои сигналы: последние 50, можно сузить по инструменту и дате
curl -s "https://123-opt.com/api/v1/signals?limit=50&symbol=EURUSD" \
-H "Authorization: Bearer $KEY"
# Только новые с прошлого раза
curl -s "https://123-opt.com/api/v1/signals?since=2026-09-01T00:00:00Z" \
-H "Authorization: Bearer $KEY"
# Свои стратегии
curl -s https://123-opt.com/api/v1/strategies -H "Authorization: Bearer $KEY"
# Статистика за период
curl -s "https://123-opt.com/api/v1/stats?days=90" -H "Authorization: Bearer $KEY"
API отдаёт только ваши данные. Маршрута, показывающего чужое, здесь нет.
Что в сигнале
{
"id": "…",
"symbol": "EURUSD",
"direction": "up",
"timeframe": "1h",
"horizonMinutes": 60,
"score": 5.5,
"generatedAt": "2026-09-10T12:00:03.120Z",
"barOpenAt": "2026-09-10T11:00:00.000Z",
"outcome": { "movePct": 0.42, "correct": true, "resolvedAt": "…" }
}
Цены входа в сигнале нет, и это не упущение: сигнал утверждает направление на горизонте, а цена зависит от того, когда вы нажмёте кнопку у брокера. outcome пуст, пока горизонт не вышел.
Webhook
Вместо опроса можно получать POST при каждом новом сигнале. Адрес задаётся в том же разделе кабинета. Требования к адресу: только https, порт 443, без логина и пароля в адресе, и адрес обязан быть публичным — внутренние сети отклоняются.
Тело запроса:
{
"event": "signal.created",
"signal": { "id": "…", "symbol": "EURUSD", "direction": "up" }
}
Заголовки:
X-Signal-Timestamp— момент отправки, секунды Unix;X-Signal-Signature— HMAC-SHA256 в шестнадцатеричном виде;X-Signal-Delivery,X-Signal-Attempt— для отладки повторов.
Проверка подписи
Подписывается строка {момент}.{тело} вашим секретом. Момент входит в подпись намеренно: без него перехваченный запрос можно повторять сколько угодно.
import { createHmac, timingSafeEqual } from 'node:crypto'
function verify(secret, rawBody, headers) {
const timestamp = Number(headers['x-signal-timestamp'])
// Старше пяти минут — отвергаем: это защита от повтора.
if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false
const expected = createHmac('sha256', secret)
.update(timestamp + '.' + rawBody)
.digest('hex')
const got = headers['x-signal-signature']
if (expected.length !== got.length) return false
return timingSafeEqual(Buffer.from(expected), Buffer.from(got))
}
import hmac, hashlib, time
def verify(secret: str, raw_body: bytes, ts: str, sig: str) -> bool:
if abs(time.time() - int(ts)) > 300:
return False
expected = hmac.new(
secret.encode(), f"{ts}.".encode() + raw_body, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, sig)
Подпись считается по сырому телу, до разбора JSON: пересобранный из объекта текст отличается пробелами и порядком полей, и подпись не сойдётся.
Повторы
Ответ 2xx означает «принято». Всё остальное — повтор: через 1, 5, 25, 120 и 360 минут. После пятой неудачи доставка помечается неуспешной, и её видно в журнале вызовов в кабинете.
Если адрес не отвечает подряд много раз, подписка выключается — долбиться в мёртвый адрес бессмысленно. Включается обратно там же, в кабинете.
Отвечайте быстро. Сервис ждёт ответа десять секунд; долгая обработка на вашей стороне должна идти после ответа, а не до него.