RMT.GG/出品者開発者ドキュメント
v1

出品者API

リスティングを自動化し、販売を履行し、注文イベントをストリームします。支払い後のオンデマンド在庫補充のためのアウトバウンドWebhookと予約エンドポイントを含みます。

RESTオープンAPI

オファーと注文のためのBearer認証付き/api/v1、ディスカバリーとレート制限ヘッダー付き。

アウトバウンドWebhook

注文とオファーのライフサイクルイベントのための署名されたHTTPS(またはDiscord)配信。

予約 / 補充

ローカル在庫が不足している場合、支払い後にサーバーからCOMPLEXストックをミントします。

構築できるもの

出品者オープンAPIは、Discordアラート、在庫同期、Zapierスタイルの自動化、またはRMT.GGの上にカスタムバックオフィスを望む出品者のためのものです。

  • オファーを管理
    ドラフトを作成し、安全なフィールドを更新し、公開し、/api/v1/offers経由でアーカイブします。
  • 販売を履行
    出品者の注文をリストし、検査し、オプションの証拠URLで配達済みとしてマークします。
  • 制限内に留まる
    各キーは、300リクエスト/分に制限されています。レスポンスにはX-RateLimit-*ヘッダーが含まれます。
  • リアルタイムで反応
    注文とオファーのイベントにサブスクライブするか、予約WebhookでCOMPLEX在庫を補充します。
  • ショップからの支払いを受け取る
    承認されたパートナーは、外部ショップからホストされたチェックアウトにバイヤーを送信し、その後注文を完了できます。

クイックスタート

開発者設定でAPIキーを作成し、ディスカバリーを呼び出してライブカタログを表示します。

  1. 1設定を開く → 開発者(別途有効化ステップは不要)。
  2. 2APIキーを作成し、シークレットを一度コピーします(rmt_sk_live_…)。シークレットマネージャーに保存します。
  3. 3GET /api/v1を呼び出し、Authorization: Bearerでスコープ、クォータ、操作を確認します。
GET/api/v1

ディスカバリードキュメント

スコープ、クォータ、Webhookイベント、および完全な操作カタログを返します。任意の有効なAPIキーが機能します。

認証

すべての/api/v1リクエストでライブシークレットキーを送信します。HTTPSのみを推奨します。公開クライアントやブラウザバンドルにキーを埋め込まないでください。

推奨ヘッダー

http
Authorization: Bearer rmt_sk_live_<prefix>_<secret>

代替ヘッダー

http
X-Api-Key: rmt_sk_live_<prefix>_<secret>

再利用可能なTypeScriptクライアント(Bearer認証、型付きエラー、429リトライ)

typescript
const API_BASE = "https://rmt.gg/api/v1";
const API_KEY = process.env.RMT_API_KEY!; // rmt_sk_live_…

export class RmtApiError extends Error {
  constructor(
    readonly status: number,
    readonly code: string | undefined,
    message: string,
    readonly retryAfterSec?: number,
  ) {
    super(message);
    this.name = "RmtApiError";
  }
}

type RmtFetchInit = RequestInit & { idempotencyKey?: string };

export async function rmtFetch<T>(path: string, init: RmtFetchInit = {}): Promise<T> {
  const headers = new Headers(init.headers);
  headers.set("Authorization", `Bearer ${API_KEY}`);
  // Alternate: headers.set("X-Api-Key", API_KEY);
  headers.set("Accept", "application/json");
  if (init.body && !headers.has("Content-Type")) {
    headers.set("Content-Type", "application/json");
  }
  if (init.idempotencyKey) headers.set("Idempotency-Key", init.idempotencyKey);

  const res = await fetch(`${API_BASE}${path}`, { ...init, headers });
  const retryAfter = Number(res.headers.get("Retry-After") ?? "");
  const body = (await res.json().catch(() => ({}))) as {
    error?: string;
    code?: string;
    retryAfter?: number;
  };

  if (res.status === 429) {
    throw new RmtApiError(
      429,
      body.code ?? "RATE_LIMITED",
      body.error ?? "Rate limited",
      Number.isFinite(retryAfter) ? retryAfter : body.retryAfter,
    );
  }
  if (!res.ok) {
    throw new RmtApiError(res.status, body.code, body.error ?? res.statusText);
  }
  return body as T;
}

export async function withRetry<T>(fn: () => Promise<T>, maxAttempts = 4): Promise<T> {
  let attempt = 0;
  for (;;) {
    try {
      return await fn();
    } catch (err) {
      attempt += 1;
      if (!(err instanceof RmtApiError) || err.status !== 429 || attempt >= maxAttempts) {
        throw err;
      }
      const waitSec = Math.max(1, err.retryAfterSec ?? 1);
      await new Promise((r) => setTimeout(r, waitSec * 1000));
    }
  }
}

漏洩時にローテーション

キーが漏洩した場合、開発者設定で無効にし、新しいものを作成します。ライブ中は無効にする前に自動化を更新してください。

スコープ

各APIキーはエンドポイントを制御するスコープを持っています。スコープが不足している場合、403 SCOPE_MISSINGが返されます。

offers:read
offers:write
orders:read
orders:write
webhooks:manage
checkout:write
  • offers:read: オファーをリストおよび取得します。
  • offers:write: オファーを作成、更新、公開、削除します。
  • orders:read: 出品者の注文をリストおよび取得します。
  • orders:write: 注文を配達済みとしてマークします。
  • webhooks:manage: 将来のオープンAPIウェブフック管理のために予約されています。今日、通知でDiscord/Telegramを設定し、開発者設定でJSONウェブフックを構成してください。
  • checkout:write: ホスティングチェックアウトセッションの作成と読み取り。管理者承認済みのパートナーのチェックアウトが必要です。

デフォルトキーのスコープ

新しいキーはoffers:read、offers:write、orders:read、およびorders:writeを受け取ります。アウトバウンドWebhookのCRUDは設定UI(セッション認証)に残ります。

オファーAPI

オファー識別子は、公開URLスラグまたは数値IDを受け入れます。レスポンスには内部IDとsellerIdは含まれません。

PATCHで変更できないもの

ストック行、オプション価格、メディア、および属性は出品者エディタ(または将来のエンドポイント)で管理されており、現在はPATCH経由ではありません。

GET/api/v1/offers
offers:read

オファーをリスト

archive=active(デフォルト)、archived、またはallでフィルタリングします。

リクエスト

  • archive
    イン
    query
    タイプ
    string
    必須
    任意
    説明
    One of "active" (default), "archived", or "all".
  • Response: { offers: Offer[], total: number }. Numeric id and sellerId are omitted.
POST/api/v1/offers
offers:write

ドラフトオファーを作成

認証された出品者が所有する空のドラフトを作成します。ボディは必要ありません。

  • No request body required.
  • Response 201: { offer: Offer }.
GET/api/v1/offers/:urlOrId
offers:read

1つのオファーを取得

公開URLスラグまたは数値IDでロードします。関係(オプション)が含まれる場合がありますが、ストックアイテムは含まれません。

リクエスト

  • urlOrId
    イン
    path
    タイプ
    string
    必須
    必須
    説明
    Offer.url slug or Offer.id.
  • Returns relations (options, etc.) when available; items are not included.
PATCH/api/v1/offers/:urlOrId
offers:write

オファーフィールドを更新

リスティングフィールドの安全なサブセットをPATCHします。アウトバウンドWebhookが構成されている場合、offer.updatedが発生します。

リクエスト

  • urlOrId
    イン
    path
    タイプ
    string
    必須
    必須
    説明
    Offer.url slug or Offer.id.
  • title
    イン
    body
    タイプ
    string
    必須
    任意
    説明
    Listing title.
  • description
    イン
    body
    タイプ
    string
    必須
    任意
    説明
    Listing description.
  • visibility
    イン
    body
    タイプ
    string
    必須
    任意
    説明
    PUBLIC | PRIVATE | UNPUBLISHED.
  • categoryId
    イン
    body
    タイプ
    number
    必須
    任意
    説明
    Catalog category id.
  • offeringId
    イン
    body
    タイプ
    number
    必須
    任意
    説明
    Catalog offering id.
  • thumbnail
    イン
    body
    タイプ
    string
    必須
    任意
    説明
    Thumbnail URL or asset reference.
  • offerType
    イン
    body
    タイプ
    string
    必須
    任意
    説明
    Offer type string used by the listing.
  • listingMode
    イン
    body
    タイプ
    string
    必須
    任意
    説明
    Listing mode (for example STANDARD, RANK_BOOST, SESSION).
  • At least one allowed field is required.
  • Emits offer.updated webhook when configured.
  • Stock is managed via GET/POST /api/v1/offers/:urlOrId/stock. Option prices, media, and attributes are not editable via this endpoint yet.
DELETE/api/v1/offers/:urlOrId
offers:write

削除またはアーカイブ

出品者UIと同じ削除/アーカイブルールです。

リクエスト

  • urlOrId
    イン
    path
    タイプ
    string
    必須
    必須
    説明
    Offer.url slug or Offer.id.
  • Response: { ok: true }.
POST/api/v1/offers/:urlOrId/publish
offers:write

オファーを公開

ドラフトを公開(または可視性を変更)します。必要なリスティングフィールドが不完全な場合、400で失敗します。

リクエスト

  • urlOrId
    イン
    path
    タイプ
    string
    必須
    必須
    説明
    Offer.url slug or Offer.id.
  • visibility
    イン
    body
    タイプ
    string
    必須
    任意
    説明
    Optional. PUBLIC (default), PRIVATE, or UNPUBLISHED.
  • Response: { offer: Offer }.
  • Fails if the listing is incomplete for publish.

ストックAPI

ティアごとの購入可能な数量を確認し、右側のリストに合った配信フィールド名を一致させてから、数量や保存されたキーとアカウントを再入荷します。

マッチングの仕組み

GET /api/v1/stock?fields=username,password は、これらのフィールドを持つスキーマのリストを見つけます。オプション名(またはoptionId)とフィールド名を使って再入荷します。内部フィールドIDは必要ありません。レスポンスには認証情報の値は含まれません。

GET/api/v1/stock
offers:read

リスト全体のストックを一覧表示

ティアごとの数量と配信フィールド名を返し、キーとアカウントを正しいオファーに一致させることができます。q、fields、stockMode、lowStockでフィルタリングします。認証情報の値は決して返しません。

リクエスト

  • q
    イン
    query
    タイプ
    string
    必須
    任意
    制限
    Max 80
    説明
    Filter by listing title or url slug.
  • fields
    イン
    query
    タイプ
    string
    必須
    任意
    説明
    Comma-separated delivery field names. The listing must have all of them (Username,Password). Names match case-insensitively.
  • stockMode
    イン
    query
    タイプ
    string
    必須
    任意
    説明
    QUANTITY or COMPLEX. Listing must have at least one option in that mode.
  • lowStock
    イン
    query
    タイプ
    number
    必須
    任意
    説明
    Keep listings that have a finite tier with available less than or equal to this number.
  • archive
    イン
    query
    タイプ
    string
    必須
    任意
    説明
    One of "active" (default), "archived", or "all".
  • Response: { offers: StockOffer[], total: number }. Numeric offer id is omitted. Option id is included so you can restock a specific tier.
  • available is the buyable count. null with unlimited true means unlimited quantity or on-demand COMPLEX inventory.
  • fields[] is the listing delivery schema (empty for quantity-only listings). Use it to map keys and accounts without field ids.
  • This endpoint never returns credential values.
GET/api/v1/offers/:urlOrId/stock
offers:read

1つのリストのストックを取得

インデックスと同じStockOfferの形状で、1つのURLまたは数値IDのためのものです。数量のみ。

リクエスト

  • urlOrId
    イン
    path
    タイプ
    string
    必須
    必須
    説明
    Offer.url slug or Offer.id.
  • Response: { offer } with the same StockOffer shape as GET /api/v1/stock.
  • Counts only. Use the seller editor to inspect saved key values.
POST/api/v1/offers/:urlOrId/stock
offers:write

リストを再入荷

数量ティア:追加、削除、または設定。保存アイテムティア:フィールド名によるアイテムオブジェクト、1つのフィールドがある場合はkeys[]、または区切られたテキスト。options[]を介して1回の呼び出しで複数のティア。dryRunはマッチングをプレビューします。onDuplicateはデフォルトでスキップします。

json
{
  "option": "1 Month",
  "add": 50
}

リクエスト

  • urlOrId
    イン
    path
    タイプ
    string
    必須
    必須
    説明
    Offer.url slug or Offer.id.
  • option
    イン
    body
    タイプ
    string
    必須
    条件付き
    説明
    Pricing option name (case-insensitive). Omit when the listing has a single tier.
  • optionId
    イン
    body
    タイプ
    number
    必須
    条件付き
    説明
    Pricing option id from GET stock. Wins over option when both are sent. Ambiguous names return 409 OPTION_AMBIGUOUS.
  • add
    イン
    body
    タイプ
    number
    必須
    条件付き
    制限
    1-1,000,000
    説明
    QUANTITY: add this many units. Fails with 400 UNLIMITED_STOCK if the tier is unlimited.
  • remove
    イン
    body
    タイプ
    number
    必須
    条件付き
    制限
    1-1,000,000
    説明
    QUANTITY: withdraw this many units. Fails with 400 INSUFFICIENT_STOCK when there is not enough.
  • set
    イン
    body
    タイプ
    number | null
    必須
    条件付き
    説明
    QUANTITY: set an absolute count. null means unlimited. Cannot go below units held in checkout.
  • items
    イン
    body
    タイプ
    object[]
    必須
    条件付き
    制限
    Max 1,000
    説明
    COMPLEX: objects keyed by delivery field name, for example { "Username": "a", "Password": "b" }. Names match case-insensitively.
  • keys
    イン
    body
    タイプ
    string[]
    必須
    条件付き
    制限
    Max 1,000
    説明
    COMPLEX: license keys when the listing has exactly one delivery field. Otherwise 400 FIELD_MAPPING_AMBIGUOUS.
  • text
    イン
    body
    タイプ
    string
    必須
    条件付き
    制限
    Max 1,000 rows
    説明
    COMPLEX: delimited paste. A header row that matches field names is detected automatically. Otherwise columns map in field sort order when the column count matches.
  • delimiter
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    Default :
    説明
    Delimiter for text. Ignored unless text is sent.
  • headers
    イン
    body
    タイプ
    string[]
    必須
    任意
    説明
    Optional column headers for text when the first line is data, not names.
  • options
    イン
    body
    タイプ
    object[]
    必須
    条件付き
    説明
    Restock several tiers in one call. Each element is the same shape as a single-option body (option, add, items, …).
  • dryRun
    イン
    body
    タイプ
    boolean
    必須
    任意
    説明
    Preview matching and counts without writing. Default false.
  • onDuplicate
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    skip (default) or error
    説明
    COMPLEX: skip existing unsold fingerprints, or fail the request with 409 DUPLICATE_ITEMS.
  • Send exactly one action per option: add, remove, set, items, keys, or text.
  • Sending items to a QUANTITY tier (or add to a COMPLEX tier) returns 400 STOCK_MODE_MISMATCH.
  • Responses never echo credential values. COMPLEX results include imported, skippedDuplicates, errors, and matchedFields.
  • Saved items are capped at 5,000 unsold rows per option. A single request may import at most 1,000 rows.

アカウントまたはキーを多数インポート

ガイド

アカウント用には items[]、単一フィールドライセンスコード用には keys[] を使用して POST /api/v1/offers/:url/stock を利用します。リクエストごとに1,000行に分割してください。

  1. 1リスティングを取得します。fields[] と stockMode を使用してアイテム、キー、または追加を選択します。
  2. 2アカウントをフィールド名でキー付けされた JSON または CSV として保存します。ライセンスキーは1行につき1つ保存します。
  3. 3最初にドライランを行います。wouldImport、skippedDuplicates、および matchedFields を確認します。
  4. 4dryRunなしで同じボディを再度POSTして在庫を更新します。

リスティングに一致するペイロードを選択

最初に GET stock を呼び出してください。fields[] に名前が複数ある場合は、それらの名前(ユーザー名、パスワード、Eメール)でキー付けされた items オブジェクトを送信します。フィールドが1つだけの場合は、keys[] で十分です。数量リスティングには items ではなく add を使用します。

リクエストごとに1,000行。ティアごとに5,000未販売アイテム。1分あたり300リクエスト。デフォルトで重複はスキップされます。

accounts.json(アカウントごとに1つのオブジェクト)

json
[
  { "Username": "player1", "Password": "secret1", "E-Mail": "[email protected]" },
  { "Username": "player2", "Password": "secret2", "E-Mail": "[email protected]" }
]

accounts.csv

csv
Username,Password,E-Mail
player1,secret1,p1@example.com
player2,secret2,p2@example.com

keys.txt(1行ごとに1つのライセンスキー)

text
AAAA-BBBB-CCCC
DDDD-EEEE-FFFF
GGGG-HHHH-IIII

cURL

bash
# Inspect field names and stockMode
curl -s -H "Authorization: Bearer rmt_sk_live_…" \
  -H "Accept: application/json" \
  https://rmt.gg/api/v1/offers/YOUR_OFFER_URL/stock

# Preview (no write)
curl -s -X POST \
  -H "Authorization: Bearer rmt_sk_live_…" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  https://rmt.gg/api/v1/offers/YOUR_OFFER_URL/stock \
  -d '{"dryRun":true,"onDuplicate":"skip","option":"Premium","items":[{"Username":"player1","Password":"secret1","E-Mail":"[email protected]"}]}'

# Apply accounts
curl -s -X POST \
  -H "Authorization: Bearer rmt_sk_live_…" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  https://rmt.gg/api/v1/offers/YOUR_OFFER_URL/stock \
  -d '{"onDuplicate":"skip","option":"Premium","items":[{"Username":"player1","Password":"secret1","E-Mail":"[email protected]"}]}'

# Apply license keys (listing must have exactly one delivery field)
curl -s -X POST \
  -H "Authorization: Bearer rmt_sk_live_…" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  https://rmt.gg/api/v1/offers/YOUR_OFFER_URL/stock \
  -d '{"option":"Steam","keys":["AAAA-BBBB-CCCC","DDDD-EEEE-FFFF"]}'

# Or paste CSV / colon-separated rows in text
curl -s -X POST \
  -H "Authorization: Bearer rmt_sk_live_…" \
  -H "Accept: application/json" \
  -H "Content-Type: application/json" \
  https://rmt.gg/api/v1/offers/YOUR_OFFER_URL/stock \
  -d '{"option":"Premium","delimiter":",","text":"Username,Password,E-Mail\nplayer1,secret1,[email protected]"}'

TypeScriptバッチインポート(リクエストごとに1,000行)

typescript
// Paste rmtFetch and withRetry from the Auth section first.
const CHUNK = 1000;

async function importRows(offerUrl: string, option: string, rows: Array<Record<string, string>>) {
  for (let i = 0; i < rows.length; i += CHUNK) {
    const items = rows.slice(i, i + CHUNK);
    const preview = await rmtFetch<{
      results: Array<{ wouldImport: number; skippedDuplicates: number; errors: string[] }>;
    }>(`/offers/${offerUrl}/stock`, {
      method: "POST",
      body: JSON.stringify({ dryRun: true, onDuplicate: "skip", option, items }),
    });
    const row = preview.results[0];
    if ((row?.errors?.length ?? 0) > 0) {
      throw new Error(row.errors.join("; "));
    }
    await withRetry(() =>
      rmtFetch(`/offers/${offerUrl}/stock`, {
        method: "POST",
        body: JSON.stringify({ onDuplicate: "skip", option, items }),
      }),
    );
  }
}

// License keys: only when GET stock.fields has exactly one name
async function importKeys(offerUrl: string, option: string, keys: string[]) {
  for (let i = 0; i < keys.length; i += CHUNK) {
    await withRetry(() =>
      rmtFetch(`/offers/${offerUrl}/stock`, {
        method: "POST",
        body: JSON.stringify({ option, keys: keys.slice(i, i + CHUNK) }),
      }),
    );
  }
}

このAPIが保護するもの

キーには offers:write が必要で、レート制限があり、自分のリスティングのみを再補充できます。GET は保存された認証情報を返しません。POST のレスポンスにはユーザー名、パスワード、またはキーの値はエコーされません。プロダクション環境では HTTPS 経由でボディを送信し、APIキーは環境変数に保持してください。

Windowsでは、curl.exeを使用してください(curlエイリアスではありません)。-d JSONを引用符で囲んで、PowerShellが分割しないようにします。

注文API

注文は出品者アカウントにスコープされています。バイヤーの請求情報は、マーケットプレイスのプライバシールールに基づいて非表示にされる場合があります。

GET/api/v1/orders
orders:read

出品者の注文をリスト

limit、offset、status、q、およびsort(newest、oldest、total_high、total_low)をサポートします。

リクエスト

  • limit
    イン
    query
    タイプ
    number
    必須
    任意
    制限
    1-100, default 20
    説明
    Page size.
  • offset
    イン
    query
    タイプ
    number
    必須
    任意
    制限
    >= 0, default 0
    説明
    Skip this many rows.
  • status
    イン
    query
    タイプ
    string
    必須
    任意
    制限
    Max 32
    説明
    Filter by order status (for example PAID, DELIVERED, COMPLETED).
  • q
    イン
    query
    タイプ
    string
    必須
    任意
    制限
    Max 80
    説明
    Search reference or related text.
  • sort
    イン
    query
    タイプ
    string
    必須
    任意
    制限
    newest (default)
    説明
    newest | oldest | total_high | total_low.
  • Response: { orders: Order[], total: number }.
GET/api/v1/orders/:uid
orders:read

1つの注文を取得

行アイテムを含む注文を返します。公開注文UIDを使用します。

リクエスト

  • uid
    イン
    path
    タイプ
    string
    必須
    必須
    説明
    Order.uid.
  • Response: { order } with line items.
  • Buyer billing fields may be redacted under marketplace-of-record privacy rules.
POST/api/v1/orders/:uid/deliver
orders:write

配達済みとしてマーク

手動履行。COMPLEX行は、必要な場合は完全に添付されている必要があります。order.deliveredが発生します。

リクエスト

  • uid
    イン
    path
    タイプ
    string
    必須
    必須
    説明
    Order.uid.
  • evidence
    イン
    body
    タイプ
    string[]
    必須
    任意
    制限
    HTTPS, max 10
    説明
    Optional screenshot or transfer-proof URLs.
  • Response: { success: true, order }.
  • COMPLEX inventory lines must be fully attached before deliver when the product requires it.
  • Emits order.delivered webhook when configured.

ホスティングチェックアウト

承認されたパートナーショップまたはバックエンドは、購入者をRMT.GGの支払いページに送信できます。私たちは記録された販売者として残り、ロックされた金額の4%を取得します。

許可リストと履行

設定の下で、ホストされたチェックアウトを適用し、そこでAPIキーとJSONウェブフックを作成します。支払い後、checkout.completedを発行します。配達値はRMT.GGの確認にのみ表示され、売り手のGETやウェブフックには含まれません。

POST/api/v1/checkout/sessions
checkout:write

ホスティングチェックアウトセッションを作成

購入者をロックされたRMT.GGの支払いページに送ります。1つのアイテム: 金額とitemName。カート: items[]には各行に名前と金額が含まれます。通貨はデフォルトでUSDです。支払い後、配達情報をコピーする必要がある場合、購入者はRMT.GGに留まります。returnUrlはショップに続きます; 配達がない場合は短いカウントダウンの後に戻します。金額、長さ、その他の制限はLimits列に記載されています。

javascript
{
  amount:   10,           // what the buyer pays
  itemName: "Gold pack",  // pay page heading
}

リクエスト

  • amount
    イン
    body
    タイプ
    number
    必須
    条件付き
    制限
    > 0, max 1,000,000
    説明
    What the buyer pays. Required for a single item. With items[], omit it or send the line sum. Mismatch: 400 AMOUNT_MISMATCH.
  • currency
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    Default USD
    説明
    ISO 4217 code such as USD or EUR.
  • itemName
    イン
    body
    タイプ
    string
    必須
    条件付き
    制限
    Max 120
    説明
    Pay page heading. Required for a single item. Alias: title. With items[], defaults to the first line name. Missing: 400 ITEM_NAME_REQUIRED.
  • title
    イン
    body
    タイプ
    string
    必須
    任意
    説明
    Alias of itemName. If both are sent, itemName wins.
  • description
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    Max 200
    説明
    Copy under the heading. If omitted, the heading is reused.
  • imageUrl
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    HTTPS, max 2048
    説明
    Product image, or fallback for lines without imageUrl. Invalid: 400 INVALID_IMAGE_URL.
  • items
    イン
    body
    タイプ
    object[]
    必須
    条件付き
    制限
    1-20 lines, JSON max 48,000
    説明
    Locked cart. Required when amount is omitted. Buyers cannot change lines. Empty: 400 INVALID_ITEMS.
  • items[].name
    イン
    body
    タイプ
    string
    必須
    必須
    制限
    Max 120
    説明
    Line title. Alias: title.
  • items[].title
    イン
    body
    タイプ
    string
    必須
    任意
    説明
    Alias of items[].name. If both are sent, name wins.
  • items[].description
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    Max 200
    説明
    Line copy under the name.
  • items[].amount
    イン
    body
    タイプ
    number
    必須
    必須
    制限
    > 0, max 1,000,000
    説明
    Unit price. Session total is sum(amount * quantity).
  • items[].quantity
    イン
    body
    タイプ
    number
    必須
    任意
    制限
    1-99, default 1
    説明
    Locked on the pay page.
  • items[].imageUrl
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    HTTPS, max 2048
    説明
    Line image. Falls back to top-level imageUrl.
  • items[].delivery
    イン
    body
    タイプ
    object[]
    必須
    任意
    制限
    Max 16 fields
    説明
    Shown after payment on RMT.GG. Seller GET and webhooks omit values.
  • items[].delivery[].name
    イン
    body
    タイプ
    string
    必須
    必須
    制限
    Max 80
    説明
    Field label, for example Code or Password.
  • items[].delivery[].type
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    text, password, textarea
    説明
    password is blurred until the buyer reveals it. Default text.
  • items[].delivery[].value
    イン
    body
    タイプ
    string
    必須
    必須
    制限
    Max 2048
    説明
    Field value. Numbers are stored as strings. Empty: 400 INVALID_DELIVERY.
  • email
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    Invalid values ignored
    説明
    Prefills the pay page. The buyer still confirms email before paying.
  • returnUrl
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    HTTPS, max 2048
    説明
    Continue-to-shop after payment. Delivery fields keep the buyer on RMT.GG with a button. No delivery: we send them back after a short countdown. If omitted, there is no shop button. http://localhost is allowed for local shops.
  • cancelUrl
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    HTTPS, max 2048
    説明
    Redirect if the buyer cancels or the session expires. If omitted, they stay on the pay page.
  • invoiceId
    イン
    body
    タイプ
    string
    必須
    任意
    制限
    Max 128
    説明
    Your shop id. Same payload returns the existing session. A different payload: 409 INVOICE_CONFLICT.
  • categorySlug
    イン
    body
    タイプ
    string
    必須
    条件付き
    制限
    With offering, or omit both
    説明
    Public root slug such as games. Used for card, PayPal, and crypto labels, not the pay page title. Wrong pair: 400 INVALID_PSP_CATEGORY.
  • offering
    イン
    body
    タイプ
    string
    必須
    条件付き
    制限
    With categorySlug, or omit both
    説明
    Catalog offering such as Mods. Mapped to labels like Games · Add-ons.
  • metadata
    イン
    body
    タイプ
    object
    必須
    任意
    制限
    Object, max 4096 chars
    説明
    Stored on the session. Not returned on seller GET.
  • Idempotency-Key
    イン
    header
    タイプ
    string
    必須
    任意
    制限
    Max 128
    説明
    Replay header. Same key and payload returns the existing session. A different payload: 409 IDEMPOTENCY_CONFLICT.

レスポンス

  • uid
    イン
    response
    タイプ
    string
    説明
    Session id. Same value as in hostedUrl / hosted_url.
  • status
    イン
    response
    タイプ
    string
    説明
    created | pending_payment | paid | canceled | expired | refunded | error. Unpaid sessions expire after 24 hours.
  • paid
    イン
    response
    タイプ
    boolean
    説明
    true only when status is paid. false for refunded, expired, canceled, and unpaid states.
  • amount
    イン
    response
    タイプ
    number
    説明
    Locked buyer total in major units.
  • currency
    イン
    response
    タイプ
    string
    説明
    ISO currency code stored on the session (for example USD).
  • itemName
    イン
    response
    タイプ
    string | null
    説明
    Pay page heading.
  • description
    イン
    response
    タイプ
    string
    説明
    Longer copy under the heading.
  • email
    イン
    response
    タイプ
    string | null
    説明
    Prefill or confirmed buyer email. Guest checkout placeholders are returned as null.
  • lang
    イン
    response
    タイプ
    string | null
    説明
    Buyer locale when known. Not a create-session field.
  • returnUrl
    イン
    response
    タイプ
    string | null
    説明
    Continue-to-shop URL stored on the session, or null.
  • cancelUrl
    イン
    response
    タイプ
    string | null
    説明
    Cancel/expiry redirect, or null.
  • invoiceId
    イン
    response
    タイプ
    string | null
    説明
    Your invoice id. Same value as externalInvoiceId.
  • externalInvoiceId
    イン
    response
    タイプ
    string | null
    説明
    Same as invoiceId (legacy alias).
  • source
    イン
    response
    タイプ
    string
    説明
    How the session was created. API sessions are "api".
  • expiresAt
    イン
    response
    タイプ
    string
    説明
    ISO timestamp. Unpaid checkouts cannot be completed after this time.
  • hostedUrl
    イン
    response
    タイプ
    string
    説明
    Pay page URL (same target as top-level hosted_url on create).
  • orderUid
    イン
    response
    タイプ
    string | null
    説明
    Marketplace order uid after payment. null until the session is paid.
  • items
    イン
    response
    タイプ
    object[]
    説明
    Locked lines: name, description, amount, quantity, imageUrl. Seller GET never includes delivery values.
  • hosted_url
    イン
    response
    タイプ
    string
    説明
    Pay page URL. Send the buyer here. Same target as hostedUrl.
  • expires_at
    イン
    response
    タイプ
    string
    説明
    ISO timestamp. Same value as expiresAt.
  • Approved partners only. Platform fee is 4% of the locked amount. Payment-method costs are absorbed by the platform.
  • After payment the buyer stays on RMT.GG so they can copy delivery fields. returnUrl is a continue button when delivery is present. With no delivery fields we send them back after a short countdown. If you omit them, they stay on the pay page after payment, cancel, or expiry.
  • Fulfill on checkout.completed. Sessions expire after 24 hours. Buyers cannot change line items. imageUrl must be HTTPS. categorySlug and offering must be sent together (or omit both); a wrong pair returns 400 INVALID_PSP_CATEGORY.
GET/api/v1/checkout/sessions/:uid
checkout:write

ホスティングチェックアウトセッションを取得

作成したセッションを返します。checkout.completedが遅延している場合はこれを使用します。paidはステータスが支払い済みのときのみtrueになります。アイテムには配達値は含まれません。

リクエスト

  • uid
    イン
    path
    タイプ
    string
    必須
    必須
    説明
    Session uid returned at create time.

レスポンス

  • uid
    イン
    response
    タイプ
    string
    説明
    Session id. Same value as in hostedUrl / hosted_url.
  • status
    イン
    response
    タイプ
    string
    説明
    created | pending_payment | paid | canceled | expired | refunded | error. Unpaid sessions expire after 24 hours.
  • paid
    イン
    response
    タイプ
    boolean
    説明
    true only when status is paid. false for refunded, expired, canceled, and unpaid states.
  • amount
    イン
    response
    タイプ
    number
    説明
    Locked buyer total in major units.
  • currency
    イン
    response
    タイプ
    string
    説明
    ISO currency code stored on the session (for example USD).
  • itemName
    イン
    response
    タイプ
    string | null
    説明
    Pay page heading.
  • description
    イン
    response
    タイプ
    string
    説明
    Longer copy under the heading.
  • email
    イン
    response
    タイプ
    string | null
    説明
    Prefill or confirmed buyer email. Guest checkout placeholders are returned as null.
  • lang
    イン
    response
    タイプ
    string | null
    説明
    Buyer locale when known. Not a create-session field.
  • returnUrl
    イン
    response
    タイプ
    string | null
    説明
    Continue-to-shop URL stored on the session, or null.
  • cancelUrl
    イン
    response
    タイプ
    string | null
    説明
    Cancel/expiry redirect, or null.
  • invoiceId
    イン
    response
    タイプ
    string | null
    説明
    Your invoice id. Same value as externalInvoiceId.
  • externalInvoiceId
    イン
    response
    タイプ
    string | null
    説明
    Same as invoiceId (legacy alias).
  • source
    イン
    response
    タイプ
    string
    説明
    How the session was created. API sessions are "api".
  • expiresAt
    イン
    response
    タイプ
    string
    説明
    ISO timestamp. Unpaid checkouts cannot be completed after this time.
  • hostedUrl
    イン
    response
    タイプ
    string
    説明
    Pay page URL (same target as top-level hosted_url on create).
  • orderUid
    イン
    response
    タイプ
    string | null
    説明
    Marketplace order uid after payment. null until the session is paid.
  • items
    イン
    response
    タイプ
    object[]
    説明
    Locked lines: name, description, amount, quantity, imageUrl. Seller GET never includes delivery values.
  • Response: { session }. Stale unpaid sessions are marked expired before they are returned.
  • Use this as a backup to checkout.completed. paid is true only when status is paid.
  • 404 NOT_FOUND if the uid is unknown or belongs to another seller.
GET/api/v1/checkout/sessions
checkout:write

請求書IDでホストされたチェックアウトセッションを検索

uidによるGETと同じセッションオブジェクトです。作成時に送信したinvoiceIdを渡してください。欠落:400 INVOICE_ID_REQUIRED。未知:404 NOT_FOUND。

リクエスト

  • invoiceId
    イン
    query
    タイプ
    string
    必須
    必須
    制限
    Max 128
    説明
    invoiceId from create. Missing: 400 INVOICE_ID_REQUIRED. Too long: 400 INVALID_INVOICE_ID.

レスポンス

  • uid
    イン
    response
    タイプ
    string
    説明
    Session id. Same value as in hostedUrl / hosted_url.
  • status
    イン
    response
    タイプ
    string
    説明
    created | pending_payment | paid | canceled | expired | refunded | error. Unpaid sessions expire after 24 hours.
  • paid
    イン
    response
    タイプ
    boolean
    説明
    true only when status is paid. false for refunded, expired, canceled, and unpaid states.
  • amount
    イン
    response
    タイプ
    number
    説明
    Locked buyer total in major units.
  • currency
    イン
    response
    タイプ
    string
    説明
    ISO currency code stored on the session (for example USD).
  • itemName
    イン
    response
    タイプ
    string | null
    説明
    Pay page heading.
  • description
    イン
    response
    タイプ
    string
    説明
    Longer copy under the heading.
  • email
    イン
    response
    タイプ
    string | null
    説明
    Prefill or confirmed buyer email. Guest checkout placeholders are returned as null.
  • lang
    イン
    response
    タイプ
    string | null
    説明
    Buyer locale when known. Not a create-session field.
  • returnUrl
    イン
    response
    タイプ
    string | null
    説明
    Continue-to-shop URL stored on the session, or null.
  • cancelUrl
    イン
    response
    タイプ
    string | null
    説明
    Cancel/expiry redirect, or null.
  • invoiceId
    イン
    response
    タイプ
    string | null
    説明
    Your invoice id. Same value as externalInvoiceId.
  • externalInvoiceId
    イン
    response
    タイプ
    string | null
    説明
    Same as invoiceId (legacy alias).
  • source
    イン
    response
    タイプ
    string
    説明
    How the session was created. API sessions are "api".
  • expiresAt
    イン
    response
    タイプ
    string
    説明
    ISO timestamp. Unpaid checkouts cannot be completed after this time.
  • hostedUrl
    イン
    response
    タイプ
    string
    説明
    Pay page URL (same target as top-level hosted_url on create).
  • orderUid
    イン
    response
    タイプ
    string | null
    説明
    Marketplace order uid after payment. null until the session is paid.
  • items
    イン
    response
    タイプ
    object[]
    説明
    Locked lines: name, description, amount, quantity, imageUrl. Seller GET never includes delivery values.
  • Same { session } body as GET /api/v1/checkout/sessions/:uid, including the fields above.
  • Prefer this when you stored your own invoice id and not the session uid.
  • 404 NOT_FOUND if no session exists for that invoice id.

アウトバウンドWebhook

設定 → 開発者でHTTPSエンドポイント(またはDiscord Webhook)を構成します。サブスクライブされたイベントが発火するとRMTがPOSTします。

order.paid
order.delivered
order.completed
order.refunded
order.disputed
offer.published
offer.updated
checkout.completed
checkout.canceled
checkout.refunded
  • JSON形式は、id、type、created、およびdataを含む構造化された封筒を投稿します。
  • Discord形式は、注文またはオファーリンクを含むリッチ埋め込みを投稿します。
  • オプションの署名は、X-RMT-TimestampとX-RMT-Signatureを使用します(予約と同じスキーム)。
  • 配信履歴は各エンドポイントの下に表示され、失敗を再試行できます。エンドポイントは繰り返し失敗した後に自動的に一時停止します。
  • ホストされたチェックアウトは、データ.checkoutと共にcheckout.completed、checkout.canceled、checkout.refundedを送信します。配達値は省略されます。マーケットプレイスの販売はorder.paidやその他のorder.*イベントを保持します。

JSON配信封筒

json
{
  "id": "whd_…",
  "type": "order.paid",
  "created": "2026-07-23T12:00:00.000Z",
  "data": {
    "order": {
      "uid": "ord_…",
      "reference": "RMT-…",
      "status": "PAID",
      "url": "https://rmt.gg/orders/ord_…",
      "items": [ /* line items with offer names */ ]
    }
  }
}

署名された配信ヘッダー

json
{
  "X-RMT-Event": "order.paid",
  "X-RMT-Delivery": "whd_…",
  "X-RMT-Timestamp": "1710000000",
  "X-RMT-Signature": "v1=abc123…"
}

チェックアウト完了ペイロード

json
{
  "id": "whd_…",
  "type": "checkout.completed",
  "created": "2026-08-14T12:00:00.000Z",
  "data": {
    "checkout": {
      "uid": "pcs_…",
      "status": "paid",
      "amount": 10,
      "currency": "USD",
      "itemName": "Gold pack",
      "description": "1000 gold for account example",
      "invoiceId": "inv-12345",
      "source": "api",
      "orderUid": "ord_…",
      "hostedUrl": "https://rmt.gg/pay/pcs_…",
      "email": "[email protected]",
      "paidAt": "2026-08-14T12:01:00.000Z",
      "expiresAt": "2026-08-15T12:00:00.000Z",
      "createdAt": "2026-08-14T12:00:00.000Z",
      "reason": null,
      "items": [
        {
          "name": "Gold pack",
          "description": "1000 gold for account example",
          "amount": 10,
          "quantity": 1,
          "imageUrl": "https://cdn.shop.example/gold.png"
        }
      ]
    }
  }
}

checkout.canceledペイロード

json
{
  "id": "whd_…",
  "type": "checkout.canceled",
  "created": "2026-08-14T12:20:00.000Z",
  "data": {
    "checkout": {
      "uid": "pcs_…",
      "status": "canceled",
      "amount": 10,
      "currency": "USD",
      "itemName": "Gold pack",
      "description": "1000 gold for account example",
      "invoiceId": "inv-12345",
      "source": "api",
      "orderUid": null,
      "hostedUrl": "https://rmt.gg/pay/pcs_…",
      "email": "[email protected]",
      "paidAt": null,
      "expiresAt": "2026-08-15T12:00:00.000Z",
      "createdAt": "2026-08-14T12:00:00.000Z",
      "reason": "buyer_canceled",
      "items": [
        {
          "name": "Gold pack",
          "description": "1000 gold for account example",
          "amount": 10,
          "quantity": 1,
          "imageUrl": "https://cdn.shop.example/gold.png"
        }
      ]
    }
  }
}

Webhook署名の確認

署名シークレットが設定されている場合、timestamp + '.' + rawBodyに対してHMAC-SHA256を計算し、v1=の後の16進数と比較します。

秘密鍵はRMTに保持されます。各署名付きPOSTにはX-RMT-Timestamp(Unix秒)とX-RMT-Signature(v1=プラス16進数)が含まれます。あなたの秘密鍵を使用して、文字列timestamp + '.' + rawBodyに対してHMAC-SHA256を計算し、v1=の後の16進数と比較します。5分以上前のタイムスタンプは拒否します。

  • 受信した生のボディバイトをそのまま読み取ります。JSONを解析してハッシュ化する前に再シリアライズしないでください。
  • X-RMT-Timestampヘッダーの値をタイムスタンププレフィックスとして使用します(同じ文字列で、再フォーマットしないでください)。
  • タイミングセーフな等価性チェックで比較します。シークレットが設定されている場合、署名が欠落しているか不一致のリクエストは拒否します。
  • リプレイを制限するために5分以上前のタイムスタンプは拒否します。同じスキームがreserve.itemおよび外向きの注文またはチェックアウトイベントにも適用されます。

TypeScript検証(タイミングセーフ比較と5分のリプレイウィンドウ)

typescript
import { createHmac, timingSafeEqual } from "node:crypto";

const MAX_AGE_SEC = 5 * 60; // reject replays older than 5 minutes

export function verifyRmtSignature(opts: {
  secret: string;
  timestamp: string | null | undefined;
  signatureHeader: string | null | undefined;
  rawBody: string; // exact POST bytes. Do not JSON.parse then re-stringify.
  nowSec?: number;
}): boolean {
  const secret = opts.secret.trim();
  const timestamp = String(opts.timestamp ?? "").trim();
  const provided = String(opts.signatureHeader ?? "").trim().replace(/^v1=/i, "");
  if (!secret || !timestamp || !provided) return false;

  const ts = Number(timestamp);
  if (!Number.isInteger(ts) || ts <= 0) return false;
  const nowSec = opts.nowSec ?? Math.floor(Date.now() / 1000);
  if (Math.abs(nowSec - ts) > MAX_AGE_SEC) return false;

  const expected = createHmac("sha256", secret)
    .update(`${timestamp}.${opts.rawBody}`)
    .digest("hex");

  const a = Buffer.from(expected, "utf8");
  const b = Buffer.from(provided.toLowerCase(), "utf8");
  if (a.length !== b.length) return false;
  return timingSafeEqual(a, b);
}

// Express / Node HTTP example:
// const rawBody = (req as { rawBody?: string }).rawBody
//   ?? JSON.stringify(req.body); // only if you captured the raw string first
// const ok = verifyRmtSignature({
//   secret: process.env.RMT_WEBHOOK_SECRET!,
//   timestamp: req.headers["x-rmt-timestamp"] as string,
//   signatureHeader: req.headers["x-rmt-signature"] as string,
//   rawBody,
// });
// if (!ok) return res.status(401).end();

TypeScriptウェブフックハンドラー

typescript
type CheckoutCompleted = {
  id: string;
  type: "checkout.completed";
  created: string;
  data: {
    checkout: {
      uid: string;
      status: "paid";
      amount: number;
      currency: string;
      itemName: string | null;
      description: string;
      invoiceId: string | null;
      source: string | null;
      orderUid: string | null;
      hostedUrl: string;
      email: string | null;
      paidAt: string | null;
      expiresAt: string | null;
      createdAt: string | null;
      reason: null;
      items: Array<{
        name: string;
        description: string | null;
        amount: number;
        quantity: number;
        imageUrl: string | null;
      }>;
    };
  };
};

type CheckoutCanceled = {
  type: "checkout.canceled";
  data: {
    checkout: {
      uid: string;
      status: "canceled" | "expired";
      invoiceId: string | null;
      reason: "buyer_canceled" | "expired";
    };
  };
};

export async function handleRmtWebhook(rawBody: string, headers: Headers) {
  const ok = verifyRmtSignature({
    secret: process.env.RMT_WEBHOOK_SECRET!,
    timestamp: headers.get("x-rmt-timestamp"),
    signatureHeader: headers.get("x-rmt-signature"),
    rawBody,
  });
  if (!ok) throw new Response("Unauthorized", { status: 401 });

  const event = JSON.parse(rawBody) as { type: string; data: Record<string, unknown> };
  switch (event.type) {
    case "checkout.completed": {
      const checkout = (event as CheckoutCompleted).data.checkout;
      if (!checkout.invoiceId || !checkout.paidAt) break;
      await fulfillShopOrder(checkout.invoiceId, checkout.orderUid);
      break;
    }
    case "checkout.canceled": {
      const checkout = (event as CheckoutCanceled).data.checkout;
      await markShopOrderCanceled(checkout.invoiceId, checkout.reason);
      break;
    }
    case "checkout.refunded":
    case "order.paid":
    case "order.delivered":
      break;
    default:
      break;
  }
}

Webhookを予約(在庫補充)

COMPLEX(ユニークユニット)リスティングの場合、RMTは支払い後に次のライセンス、アカウント、またはキーをミントするためにあなたのHTTPSエンドポイントにPOSTできます。ローカル在庫が不足している場合。

支払い安全な失敗

エンドポイントがタイムアウトするか無効なデータを返すと、注文はPAIDのままです。バイヤーは請求され、注文にエラーが表示され、予約を再試行するか手動でキーを添付できます。

設定方法

  1. アイテムフィールド(例えばライセンス)を含む複雑なオファーを作成します。
  2. アイテムステップで、オンデマンドインベントリエンドポイントを有効にし、公開HTTPS URLを貼り付けます。
  3. オプションで署名シークレットを設定し、RMTがすべての呼び出しでX-RMT-TimestampとX-RMT-Signatureを送信するようにします。
  4. テストを実行するか(サンプルJSONを貼り付け)、レスポンスパスをアイテムフィールドにマッピングし、保存します。
  5. リスティングを公開します。バイヤーは空のローカルストックで購入でき、支払い後にキーが作成されます。
  • ローカル在庫が常に優先され、Webhookは不足分のみを補充します。
  • オファーエディタのアイテムステップで、オファーレベルのデフォルトを構成するか、価格オプションごとに上書きします。
  • HTTPSのみ。オプションのHMAC署名はアウトバウンドWebhookと一致します(X-RMT-Event: reserve.item)。
  • エディタでのテストはdryRun: trueを送信します。注文ページでは、エンドポイントを修正した後に「予約を再試行」を使用します。

標準POSTボディ(切り詰め)

json
{
  "id": "rsv_…",
  "type": "reserve.item",
  "order": { "uid": "ord_…", "reference": "RMT-…", "url": "https://rmt.gg/orders/ord_…" },
  "offer": { "url": "my-offer", "title": "Game key", "pageUrl": "https://rmt.gg/offers/my-offer" },
  "option": { "id": 1, "name": "Standard" },
  "fields": [{ "id": 10, "name": "License", "type": "text", "required": true }],
  "quantity": 1
}

リクエストヘッダー(署名シークレットが設定されている場合)

json
{
  "Content-Type": "application/json",
  "X-RMT-Event": "reserve.item",
  "X-RMT-Delivery": "rsv_…",
  "X-RMT-Timestamp": "1710000000",
  "X-RMT-Signature": "v1=abc123…"
}

便利なレスポンス

json
{
  "entries": [
    { "name": "License", "value": "AAAA-BBBB-CCCC" }
  ]
}

マッピングされたJSONフィールド(レスポンスマップパスは$.licenseのようになります)

json
{
  "license": "AAAA-BBBB-CCCC",
  "email": "[email protected]",
  "password": "temporary-pass"
}

署名シークレットの検証方法

オファーにシークレットを設定した場合、すべてのreserve POSTは署名されます。HMAC-SHA256(secret, timestamp + '.' + rawBody)を再計算し、v1=プレフィックスを削除した後にX-RMT-Signatureと比較します。シークレット自体はリクエストに含まれません。

完全な検証例を参照してください

TypeScriptリザーブハンドラー(検証後、エントリを返す)

typescript
type ReserveRequest = {
  id: string;
  type: "reserve.item";
  dryRun?: boolean;
  quantity: number;
  fields: Array<{ name: string; required?: boolean }>;
};

export async function handleReserve(rawBody: string, headers: Headers) {
  const ok = verifyRmtSignature({
    secret: process.env.RMT_RESERVE_SECRET!,
    timestamp: headers.get("x-rmt-timestamp"),
    signatureHeader: headers.get("x-rmt-signature"),
    rawBody,
  });
  if (!ok) return new Response("Unauthorized", { status: 401 });

  const body = JSON.parse(rawBody) as ReserveRequest;
  if (body.type !== "reserve.item") {
    return Response.json({ error: "Unexpected event" }, { status: 400 });
  }

  const qty = Number(body.quantity);
  if (!Number.isInteger(qty) || qty < 1) {
    return Response.json({ error: "Invalid quantity" }, { status: 400 });
  }

  if (body.dryRun) {
    return Response.json({
      entries: [{ name: "License", value: "TEST-AAAA-BBBB" }],
    });
  }

  const license = await mintLicense(); // your inventory
  return Response.json({
    entries: [{ name: "License", value: license }],
  });
}

支払い前に予約を呼び出さないでください

RMTは支払いが成功した後にのみエンドポイントを呼び出すため、放棄されたチェックアウトはライセンスを消費しません。

エラーとレート制限

エラーはJSON { error, code? }を返します。オープンAPIトラフィックは、APIキーごとに300リクエスト/分に制限されています。

  • API_KEY_REQUIRED
    401

    AuthorizationまたはX-Api-Keyヘッダーが不足しています。

  • API_KEY_INVALID
    401

    キーが不明、取り消された、期限切れ、または開発者アクセスが停止されています。

  • SCOPE_MISSING
    403

    キーがエンドポイントに必要なスコープを欠いています。

  • RATE_LIMITED
    429

    リクエストが多すぎます。Retry-AfterとX-RateLimit-Resetを尊重してください。

  • CHECKOUT_PARTNER_NOT_APPROVED
    403

    この販売者はホスティングチェックアウトの承認を受けていません。

  • INVALID_JSON
    400

    リクエストボディはJSONでなければなりません。

  • INVALID_AMOUNT
    400

    金額は0より大きく、最大1,000,000でなければなりません。

  • UNSUPPORTED_CURRENCY
    400

    通貨はサポートされているISOコードではありません。

  • INVALID_RETURN_URL
    400

    returnUrlとcancelUrlはhttpsでなければなりません(http://localhostはローカルショップ用に許可されています)。

  • INVALID_PSP_CATEGORY
    400

    categorySlugとofferingは一緒に送信され、カタログペアと一致する必要があります。

  • INVALID_INVOICE_ID
    400

    invoiceIdは128文字を超えています。

  • INVOICE_ID_REQUIRED
    400

    GET /checkout/sessionsはクエリパラメータとしてinvoiceIdが必要です。

  • INVALID_IDEMPOTENCY_KEY
    400

    Idempotency-Keyは128文字を超えています。

  • INVALID_METADATA
    400

    metadataはJSONオブジェクトでなければならず、配列やプリミティブではありません。

  • METADATA_TOO_LARGE
    400

    シリアライズされたmetadataは4096文字を超えています。

  • IDEMPOTENCY_CONFLICT
    409

    Idempotency-Keyが異なる金額、通貨、またはアイテムで再利用されました。

  • INVOICE_CONFLICT
    409

    invoiceIdが異なる金額、通貨、またはアイテムで再利用されました。

  • INVALID_IMAGE_URL
    400

    imageUrlはhttpsのURLでなければなりません。

  • ITEM_NAME_REQUIRED
    400

    アイテム名(またはタイトル)は、アイテムが省略された場合に必要です。

  • INVALID_ITEMS
    400

    itemsはロックされたラインアイテムの空でない配列でなければなりません(最大20)。各ラインにはnameとamountが必要です。

  • TOO_MANY_ITEMS
    400

    itemsは20行を超えることはできません。

  • AMOUNT_MISMATCH
    400

    amountは各行の金額と数量の合計と等しくなければなりません。

  • INVALID_DELIVERY
    400

    配信フィールドが無効です。各フィールドにはname(最大80)とvalue(最大2048)が必要です。typeはtext、password、またはtextarea(デフォルトはtext)でなければなりません。1行あたり最大16フィールドです。

  • ITEMS_TOO_LARGE
    400

    シリアライズされたアイテムのJSONが48,000文字を超えています。

  • NOT_FOUND
    404

    この売り手に対するuidまたはinvoiceIdに一致するホステッドチェックアウトセッションはありません。

  • RESERVE_FAILED
    400

    予約Webhookがタイムアウトした、無効なデータを返した、または必須フィールドが不足していました。

  • OPTION_AMBIGUOUS
    409

    その名前に一致する価格オプションが複数あります。GET stockからoptionIdを渡してください。

  • OPTION_NOT_FOUND
    404

    このリストにそのIDまたは名前に一致する価格オプションはありません。

  • OPTION_REQUIRED
    400

    このリストには複数の価格オプションがあります。optionまたはoptionIdを渡してください。

  • UNKNOWN_FIELD
    400

    フィールド名がこのリストの配信スキーマと一致しません。

  • FIELD_MAPPING_AMBIGUOUS
    400

    列またはキーを配信フィールドにマッピングできませんでした。ヘッダーを送信するか、フィールド名でキー付けされたアイテムオブジェクトを使用してください。

  • STOCK_MODE_MISMATCH
    400

    そのペイロードはオプションのストックモード(数量対保存アイテム)と一致しません。

  • IMPORT_TOO_LARGE
    400

    再入荷リクエストは、オプションごとに最大1,000の保存アイテムをインポートできます。

  • OPTION_ITEM_CAPACITY
    400

    この価格オプションにはすでに最大の5,000の未販売保存アイテムがあります。

  • DUPLICATE_ITEMS
    409

    onDuplicate=errorで、少なくとも1つのアイテムがこのオプションにすでに存在します。

  • UNLIMITED_STOCK
    400

    このオプションには無制限の数量があります。まず有限のカウントに切り替えるためにsetを使用してください。

  • INSUFFICIENT_STOCK
    400

    削除するための数量ストックが不足しています。

  • STOCK_HELD_IN_CHECKOUT
    400

    チェックアウトで予約されている数量を下回ることはできません。

  • INVALID_RESTOCK
    400

    再入荷ボディに必要なアクションが欠けているか、1つのオプションにadd/itemsが組み合わされています。

429を処理する

Retry-After秒を使用してバックオフします。制限を回避するためにキーをローテーションしないでください。制限はキーごとであり、すべての出品者に対してフラットです。

成功したレスポンスにはX-RateLimit-Limit、X-RateLimit-Remaining、およびX-RateLimit-Resetが含まれます。

自動化の準備はできましたか?

開発者設定でキーを作成し、通知の下でDiscordまたはTelegramを接続してください。