Developers

ORGON API

Custodial wallets для крипто-обменников, банков и бирж. Один REST API — выпустить депозит-адреса своим клиентам, отслеживать поступления через webhooks, отправлять транзакции по подписи.

Не пишите HMAC сами — используйте SDK

npm i @orgon/sdk

import { OrgonClient } from "@orgon/sdk";
const orgon = new OrgonClient({ apiKey: process.env.ORGON_KEY!, secret: process.env.ORGON_SECRET! });
await orgon.users.create({ external_id: "u123", email: "[email protected]" });
Quickstart

Первая транзакция — за 5 шагов

1

Выпустите API-ключ

Settings → API ключи → Выпустить. Сохраните secret — он показывается один раз.

2

Зарегистрируйте конечного пользователя

POST /v1/users с external_id (ваш id юзера) и email. Идемпотентно — можно вызывать на каждом логине.

3

Создайте депозит-кошелёк

POST /v1/wallets {network, end_user_id}. На email юзера придёт ссылка подтверждения от Safina — пользователь её кликает.

4

Настройте webhook

PUT /v1/webhooks/config {url, secret}. Мы будем POST'ить события wallet.deposit.detected, transaction.confirmed, и т.д.

5

Отправляйте транзакции

POST /v1/transactions {wallet_id, to_address, amount}. Подпись своим EC ключом → broadcast → Tronscan через ~30 сек.

Authentication

HMAC-подпись каждого запроса

Получите пару key_pub/secret в Настройках → API ключи. Secret отображается один раз — сохраните его в свой vault. Дальше каждый запрос к /v1/* подписывайте четырьмя заголовками:

http
X-ORGON-Key:              okl_a1b2c3d4...
X-ORGON-Timestamp:        1715690000000
X-ORGON-Nonce:            e3b0c442-98fc-1c14-9afb-f4c8996fb924
X-ORGON-Signature:        7d865e959b2466918c9863afca942d0fb89d7c9ac0c99bafc3749504ded97730
# Опционально на mutating вызовах — гарантирует, что повтор
# при network-сбое не создаст дубль транзакции/кошелька:
X-ORGON-Idempotency-Key:  8c1d4f0e-4af3-49e2-b8a3-1f0c0a9d1234

Подпись — hex(HMAC-SHA256(secret, message)), где message = конкатенация:

text
<timestamp>\n<nonce>\n<METHOD>\n<path>\n<raw body>

TypeScript

ts
import crypto from "node:crypto";
import { randomUUID } from "node:crypto";

const KEY_PUB = process.env.ORGON_KEY!;
const SECRET = process.env.ORGON_SECRET!;
const BASE = "https://orgon.asystem.ai";

async function call(method: string, path: string, body?: unknown) {
  const ts = Date.now().toString();
  const nonce = randomUUID();
  const raw = body ? JSON.stringify(body) : "";
  const msg = `${ts}\n${nonce}\n${method}\n${path}\n${raw}`;
  const sig = crypto.createHmac("sha256", SECRET).update(msg).digest("hex");

  const res = await fetch(BASE + path, {
    method,
    headers: {
      "Content-Type": "application/json",
      "X-ORGON-Key": KEY_PUB,
      "X-ORGON-Timestamp": ts,
      "X-ORGON-Nonce": nonce,
      "X-ORGON-Signature": sig,
    },
    body: raw || undefined,
  });
  if (!res.ok) throw new Error(`ORGON ${res.status}: ${await res.text()}`);
  return res.json();
}

// Usage:
//   const ping = await call("GET", "/v1/ping");
//   const user = await call("POST", "/v1/users", { external_id: "u123", email: "[email protected]" });

Python

py
import hmac, hashlib, json, time, uuid
import httpx

KEY_PUB = "okl_a1b2c3..."
SECRET  = b"oksl_8e9f..."
BASE    = "https://orgon.asystem.ai"

def call(method: str, path: str, body=None):
    ts = str(int(time.time() * 1000))
    nonce = str(uuid.uuid4())
    raw = json.dumps(body, separators=(",", ":")) if body is not None else ""
    msg = f"{ts}\n{nonce}\n{method}\n{path}\n{raw}".encode()
    sig = hmac.new(SECRET, msg, hashlib.sha256).hexdigest()
    headers = {
        "Content-Type": "application/json",
        "X-ORGON-Key": KEY_PUB,
        "X-ORGON-Timestamp": ts,
        "X-ORGON-Nonce": nonce,
        "X-ORGON-Signature": sig,
    }
    r = httpx.request(method, BASE + path, headers=headers, content=raw or None)
    r.raise_for_status()
    return r.json()

# Usage:
#   ping = call("GET", "/v1/ping")
#   user = call("POST", "/v1/users", {"external_id": "u123", "email": "[email protected]"})
Webhooks

Event-driven нотификации

Зарегистрируйте URL через PUT /v1/webhooks/config. Когда происходит событие — например, на адрес клиента приходит TRX или USDT — ORGON отправит POST на ваш endpoint с подписанным телом. Доставка надёжная: при ошибке мы повторяем с экспоненциальным backoff (30s → 2m → 10m → 1h → 6h, 6 попыток).

wallet.activated

Safina выдала on-chain address для кошелька.

wallet.deposit.detected

На адрес пришёл входящий on-chain transfer (TRX, USDT, …).

transaction.broadcasted

Исходящая транзакция отправлена в сеть, появился tx_hash.

transaction.confirmed

Транзакция подтверждена сетью (нужное число блоков).

transaction.failed

Транзакция отменена или отклонена сетью.

user.created

Эхо после POST /v1/users — для аудита у вас.

Проверка подписи (Node.js)

ts
import crypto from "node:crypto";
import type { Request, Response } from "express";

// Same secret you set via PUT /v1/webhooks/config { secret }
const WEBHOOK_SECRET = process.env.WEBHOOK_SECRET!;

export function orgonWebhook(req: Request, res: Response) {
  const ts = req.header("X-ORGON-Webhook-Timestamp") ?? "";
  const sig = req.header("X-ORGON-Webhook-Signature") ?? "";
  const raw = (req as any).rawBody as Buffer;  // capture raw body in middleware

  // 5-min drift window — protects against replay of an old payload.
  if (Math.abs(Date.now() - Number(ts)) > 5 * 60 * 1000) {
    return res.status(401).end();
  }

  const expected = crypto
    .createHmac("sha256", WEBHOOK_SECRET)
    .update(`${ts}\n`)
    .update(raw)
    .digest("hex");

  if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
    return res.status(401).end();
  }

  const event = JSON.parse(raw.toString("utf8"));
  switch (event.type) {
    case "wallet.deposit.detected":
      // credit user balance, mark order paid, etc.
      break;
    case "transaction.confirmed":
      // mark payout completed
      break;
  }
  res.json({ ok: true });
}

Тестовый event без реального deposit

Чтобы убедиться что ваш endpoint валидно принимает события до прихода первого реального deposit — поставьте в очередь synthetic event:

bash
# из вашей машины, через signed SDK-call или curl:
curl -X POST https://orgon.asystem.ai/v1/webhooks/test \
  -H "X-ORGON-Key: $ORGON_KEY" \
  -H "X-ORGON-Timestamp: $TS" \
  -H "X-ORGON-Nonce: $NONCE" \
  -H "X-ORGON-Signature: $SIG" \
  -d '{"event_type":"webhook.test","payload":{"hello":"world"}}'
# → { "delivery_id": "...", "queued": true }
Ошибки

Стандартный формат ответа

Любая 4xx/5xx-ошибка из /v1/* приходит в одном виде:

json
{
  "error":   "<machine-readable code>",
  "message": "<human-readable description>",
  "request_id": "92f3e4a7…",
  "details": [ ... ]    // только для 422 (валидация)
}

В каждом ответе также есть заголовок X-Request-Id. Если что-то идёт не так — пришлите этот id в support, и мы найдём весь путь запроса в логах.

unauthorizedHTTP 401

Подпись не прошла или ключ отозван / истёк.

rate_limitedHTTP 429

Достигнут лимит вашего pricing-плана за день.

sandbox_restrictedHTTP 400

Sandbox-ключ обратился к mainnet-сети.

validation_errorHTTP 422

Тело запроса не прошло pydantic-валидацию. См. `details`.

not_foundHTTP 404

Ресурс не существует или принадлежит другому merchant'у.

conflictHTTP 409

Идентификатор уже занят (например slug merchant'а).

upstream_errorHTTP 502

Safina вернула ошибку при выполнении операции.

internal_errorHTTP 500

Непредвиденная ошибка на нашей стороне. Пришлите request_id.

Sandbox

Тестирование на testnet

При выпуске ключа поставьте галочку sandbox. Ключи в sandbox имеют префикс okt_* и работают только с testnet сетями (Tron Nile — 5010, BTC test — 1010, ETH Sepolia — 3010). Тестовые TRX можно получить через Nile faucet.

Карта API

Что доступно

End-users

  • POST /v1/users
  • GET /v1/users/{id}
  • GET /v1/users
  • PATCH /v1/users/{id}

Wallets

  • POST /v1/wallets
  • GET /v1/wallets/{id}
  • GET /v1/users/{id}/wallets

Transactions

  • POST /v1/transactions
  • POST /v1/transactions/{id}/sign
  • GET /v1/transactions/{id}
  • GET /v1/transactions

Deposits (incoming)

  • GET /v1/wallets/{id}/deposits
  • GET /v1/users/{id}/deposits

Webhooks

  • GET /v1/webhooks/config
  • PUT /v1/webhooks/config
  • GET /v1/webhooks/deliveries

Meta

  • GET /v1/health
  • GET /v1/networks
  • GET /v1/ping

Полная схема всех полей и кодов ошибок — Swagger UI.