Skip to main content
Silver 以上のメンバーシップが必要です — 申請時だけでなく、認証済みのすべての呼び出しでも同様です。キー所有者のメンバーシップが失効すると、再加入するまでプロジェクトは凍結されます。
アプリケーションが Discord の中からユーザーの Vito 残高を扱うための REST API です。残高の読み取り、請求、付与、ユーザー間の移動ができます。すべてのエンドポイントは JSON を返し、/v1 配下でバージョン管理されています。
Vito が実際のお金と行き来することは決してありません。 Vetox の残高どうしの間だけを移動します。
すべてのエンドポイントは https://api.vetox.io/public/vito の下にあります。このページに書かれているパス(/v1/deduct/v1/balance/:discordId など)は、この接頭辞からの相対パスです。SDK はこれを自動的に付けます。自分で呼び出す場合は完全な URL を使ってください:
https://api.vetox.io/v1/deduct はルートとして存在せず、404 を返します。
Node.js で開発していますか? REST 呼び出しを自分で組み立てる必要はありません。公式パッケージ @vetox-bot/vito をお使いください。9 つのエンドポイントすべてと webhook の検証をカバーし、冪等性と再試行も引き受けます。下の公式 Node.js SDK を参照してください。

アクセスの取得

アクセスはプロジェクト単位で付与されます。次の 4 つすべてが必要です。
1

有効な Silver 以上のメンバーシップ

承認時だけでなく、呼び出しのたびに確認されます。
2

承認された開発者申請

ダッシュボードの Vito API ページから送信します。Vetox チームが手作業で審査します。
3

API 開発者規約への同意

申請を送信する際に承諾します。
4

プロジェクトに必要なスコープ

記載内容に基づいて Vetox チームが付与します。
何を作るのか、キーをどう保管するのかを具体的に書いてください。却下されるのは決まって曖昧な申請です。

スコープ

認証

シークレットキーを Bearer トークンとして送信します。
任意で追加できる 2 つの層が、プロジェクトをさらに堅牢にします。
  • IP 許可リスト — 呼び出しを特定のサーバー IP に限定します
  • レート制限 — 所有者のメンバーシップ階層に応じて上がる、プロジェクトごとの上限です

キーのローテーションと保管

キーと署名シークレットが表示されるのは一度だけです。 承認後、API キータブでそれらを表示するための7 日間の猶予があります。Vetox はハッシュしか保持しておらず再表示はできません。期間を過ぎた場合はローテーションして取得し直してください。
  • サーバー側だけに保管してください — 手にした者は誰でもあなたのユーザーに請求できます
  • ローテーションは API キータブから行います。以前のキーは 24 時間の猶予期間にわたり有効なままなので、停止なしでデプロイできます
  • webhook の署名シークレットは別枠でローテーションされ、独自の 24 時間の重複期間を持ちます
  • 漏洩した場合は直ちにローテーションしてください

公式 Node.js SDK

公式パッケージ @vetox-bot/vito は、9 つのエンドポイントすべてと webhook の検証をラップします。Idempotency-Key ヘッダー、指数バックオフ付きの再試行、タイムアウト、エラーの分類を代わりに引き受けます。

インストール

Node.js 20 以降が必要です。このパッケージには実行時依存が一切ありません — 組み込みの fetchnode:crypto を使います — そして ESM と CommonJS の両方を、完全な TypeScript 型定義付きで提供します。

初期化

apiKey を省略すると、SDK は環境変数 VITO_API_KEY を読み取ります。キーの形式は生成時に検証されるため、不正な形式のキーはネットワーク往復と 401 を無駄に費やすことなく即座に失敗します。
このキーはお金を動かします。必ずサーバー側に保管し、クライアントのバンドルやブラウザーには決して置かないでください。console.log(vito) はキーではなく [redacted] を出力し、SDK はローカル以外のホストに対する http://baseUrl をすべて拒否するため、キーが平文で流れることはありません。

クライアントのオプション

利用できるメソッド

どのメソッドも、包装を解いた data フィールドをそのまま返します。successdata を自分で取り出す必要はありません。さらにすべてのメソッドが呼び出し単位のオプション { timeoutMs, maxRetries, signal, headers } を受け取り、書き込み系はこれに加えて { idempotencyKey } も受け取ります。

使用例

起動時にキーを確認する

残高を読み取る

ユーザーに請求する(商品の販売)

ここでは注文を保留中として記録するだけにしてください。請求はまだ発生していません。引き渡しは confirmation.completed の webhook で行い、このレスポンスでは行わないでください。

ユーザーに付与する

取引をたどる

冪等性と再試行

SDK はすべての書き込みdeductcredittransfer)で Idempotency-Key ヘッダーを送ります。指定しなかった場合は呼び出しごとに一度だけ生成し、再試行のたびにまったく同じキーを送り直すため、再試行によって同じ操作が二重に決済されることはありません。 同じ論理操作が別のプロセスから再実行されうる場合 — ジョブランナー、キューの再配信、定期実行など — は、自分でキーを渡してください。
auth.rotateKey() は意図的に除外しています。ここで再試行すると 2 本目のキーが発行され、最初の試行が返したキーが無効になってしまうためです。
Retry-AftermaxRetryDelayMs より長い場合(つまり 1 時間あたりの枠を本当に使い切った場合)、SDK はリクエストの時間予算を眠って費やすのではなく、直ちに VitoRateLimitError を投げます。

SDK による webhook の検証

constructEvent は 5 分のリプレイウィンドウを確認し、ヘッダー内のすべての h1 署名と一定時間で比較します。そのためローテーション時の 24 時間の重複期間も自動的に通過し、続けてペイロードを解析して型付きのイベントを返します。 Next.js(App Router)のルートハンドラーでは、生のボディを自分で読む方の関数を使ってください。
署名が対象とするのは生のバイト列です。Express では webhook のルートに express.raw({ type: 'application/json' }) を設定してください。express.json() はボディを消費してしまい、以降の検証がすべて失敗します。Next.js では constructEventFromRequest の前に request.json() を呼ばないでください。
配信は最低 1 回です。副作用を実行する前に event.eventId で重複を排除してください。

エラー処理

SDK が投げるものはすべて VitoError を継承し、codestatustyperequestIdretryable を持ちます。
requestId は必ずログに残してください。サポートが特定の呼び出しを追跡するために必要なのはこの値です。

呼び出しのキャンセル

キャンセルは待機中の再試行も停止し、コード VITO_SDK_ABORTEDVitoConnectionError を投げます。

ユーザーへの請求

キーだけでユーザーの Vito を動かすことはできません。 すべての請求には、vetox.io 上でユーザーがウォレットの PIN によって承認する必要があります。あなたのアプリ内でも Discord 内でも行われません。
1

アプリが POST /v1/deduct を呼び出す

ユーザー、金額、発生元の guildId、商品の詳細を添えて送信します。
2

Vito が confirmUrl を返す

保留中の確認で、有効期間は 10 分です。ユーザーには DM も届きます。
3

ユーザーが PIN で承認する

vetox.io 上で行います。
4

Vito が決済して通知する

残高が引き落とされ、取引が記録され、設定していれば署名付き webhook が送信されます。
5

アプリが検証して完了する

署名を確認してから、コンテンツを解放するか商品を引き渡します。
処理を完了させるのは confirmation.completed を受け取ったときだけにしてください。/deduct のレスポンスで完了させてはいけません。その時点では請求はまだ確定していません。

リクエストパラメーター — /v1/deduct

ユーザーへの付与

POST /v1/add自分の残高を原資にユーザーへ Vito を付与します。報酬や返金に使います。フィールドは guildIdproduct を除いて /deduct と同じです。
請求とは異なり、付与に確認ステップはありません — 即時に決済されます。credit:create スコープと十分な残高が必要で、足りない場合は 402 VITO_INSUFFICIENT_OWNER_FUNDS が返ります。

手数料

すべての請求は、プラットフォーム手数料を差し引いてあなたに支払われます。アプリ内の Vito 送金と同じ料率で、あなたのメンバーシップ階層に基づきます。
5 Vito 以下の金額は手数料無料で、/v1/add による付与は常に手数料無料です。

Webhook

設定タブで https のコールバック URL を 1 つ以上追加します。確認が終了状態に達するたびに、Vito が署名付きの POST を送信します。
webhook が送信されるのは、プロジェクトにコールバック URL 署名シークレットの両方がある場合だけです。シークレット(whsec_…)は API キータブから一度だけ表示できます。

署名の検証

すべての配信には X-Vito-Signature ヘッダーが付きます。
さらに 2 つのヘッダーが各配信に付随します。再送では同じ id が送られるため、X-Vito-Event-Id を重複排除キーとして使ってください。
署名シークレットのローテーション中、ヘッダーは複数の署名を含みます。新しいものが先です。
h1いずれかが一致すれば配信を受け入れてください。最初の 1 つしか読まない検証コードは、新しいシークレットをデプロイし終えるまですべての webhook を拒否します。これでは 24 時間の重複期間を設けた意味がなくなります。
Node.js の場合: @vetox-bot/vitoWebhooks.constructEvent が以下をすべて代行します — リプレイウィンドウ、すべての h1 署名との照合、一定時間での比較 — そのうえで型付きのイベントを返します。下のコードは自前で実装する場合や他の言語向けです。
署名シークレットで <ts>:<rawBody> の HMAC を再計算し、一定時間で比較してください。
JSON パースやミドルウェアが書き換える前の、生のボディに対して検証してください。

イベント

イベントは 5 種類で、すべて同じペイロード形式を共有します。結果は data.status が保持します。
webhook はバックオフを挟んで 5 回再試行されます。速やかに 2xx を返し、引き渡し処理は非同期で行ってください。

エラーコード

すべてのレスポンスはエンベロープに包まれます。成功時は data、失敗時は error を持ち、両方が同時に入ることはありません。
すべてのコードには VITO_ が前置されます。完全な文字列で判定してください。RATE_LIMITEDFORBIDDEN が単独で返ることはありません。

レート制限

さらに、1 分あたりの上限の半分(下限 30)が IP ごとの制限として適用されます。
高負荷時、書き込みエンドポイントはフェイルクローズします — 二重支払いのリスクを取るより請求を拒否します。読み取りはフェイルオープンします。拒否された書き込みは「起きなかった」ものとして扱い、再試行してください。
再試行を安全に重複排除するには Idempotency-Key ヘッダーを送信してください。

エンドポイント

制限

  • 確認は 10 分で失効します — 未確認のリクエストは破棄されたものとして扱ってください
  • amount は正の整数である必要があります
  • metadata は 10 キーまでです
  • 1 取引あたりと 1 日あたりの上限は Vetox チームが設定し、設定タブに読み取り専用で表示されます

セキュリティチェックリスト

API キーと署名シークレットをクライアントコードに置いてはいけません。いずれかが漏洩したら直ちにローテーションしてください。
生のボディに対して署名を確認し、約 5 分より古い配信は拒否してください。
/deduct のレスポンスで引き渡してはいけません。請求は confirmation.completed で初めて確定します。
実際に使うスコープだけを申請し、IP 許可リストを有効にしてください。

トラブルシューティング

所有者のメンバーシップが失効しています。呼び出しのたびに再確認されます。
復元はできません — 保存されているのはハッシュだけです。ローテーションして新しいものを取得してください。
24 時間の重複期間中は両方のシークレットを受け入れてください。
ユーザーが承認していません。確認は 10 分で失効します。
プロジェクトにはコールバック URL と署名シークレットの両方が必要です。片方だけでは何も配信されません。
決済手数料です。避けるには 5 Vito 以下の金額を使うか、価格に織り込んでください。
/v1/add はあなた自身の残高を原資とし、無から作られるものではありません。残高を補充してください。

Vito

残高、PIN、手数料について。

支払いリクエスト

請求したときにユーザーに見えるもの。

npm 上の @vetox-bot/vito

公式 Node.js パッケージ — インストール 1 回で完全に統合。