/api/v1발견 문서
범위, 할당량, 웹훅 이벤트 및 전체 작업 카탈로그를 반환합니다. 유효한 API 키는 모두 작동합니다.
판매자 오픈 API는 Discord 알림, 재고 동기화, Zapier 스타일 자동화 또는 RMT.GG 위에 맞춤형 백오피스를 원하는 판매자를 위한 것입니다.
개발자 설정에서 API 키를 생성한 후, 디스커버리를 호출하여 실시간 카탈로그를 출력하세요.
/api/v1범위, 할당량, 웹훅 이벤트 및 전체 작업 카탈로그를 반환합니다. 유효한 API 키는 모두 작동합니다.
모든 /api/v1 요청에 대해 라이브 비밀 키를 전송합니다. HTTPS만 선호합니다. 공개 클라이언트나 브라우저 번들에 키를 포함하지 마십시오.
선호하는 헤더
Authorization: Bearer rmt_sk_live_<prefix>_<secret>대체 헤더
X-Api-Key: rmt_sk_live_<prefix>_<secret>재사용 가능한 타입스크립트 클라이언트 (Bearer 인증, 타입 오류, 429 재시도)
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: 향후 Open API 웹훅 관리를 위해 예약되었습니다. 오늘 알림에서 Discord/Telegram을 설정하고 개발자 설정에서 JSON 웹훅을 구성하세요.checkout:write: 호스팅 결제 세션을 생성하고 읽습니다. 관리자가 승인한 파트너 결제가 필요합니다.기본 키 범위
새 키는 offers:read, offers:write, orders:read 및 orders:write를 받습니다. 아웃바운드 웹훅 CRUD는 설정 UI(세션 인증)에 남아 있습니다.
제안 식별자는 공개 URL 슬러그 또는 숫자 ID를 수용합니다. 응답에는 내부 ID 및 sellerId가 생략됩니다.
PATCH로 변경할 수 없는 것
재고 행, 옵션 가격, 미디어 및 속성은 판매자 편집기(또는 미래의 엔드포인트)에서 관리되며, 오늘은 PATCH를 통해 변경할 수 없습니다.
/api/v1/offersarchive=active(기본값), archived 또는 all로 필터링합니다.
요청
archive| 이름 | 안 | 유형 | 필수 | 설명 |
|---|---|---|---|---|
archive | query | string | 선택 사항 | One of "active" (default), "archived", or "all". |
/api/v1/offers인증된 판매자가 소유한 빈 초안을 생성합니다. 본문은 필요하지 않습니다.
/api/v1/offers/:urlOrId공식 URL 슬러그 또는 숫자 ID로 로드합니다. 관계(옵션)가 포함될 수 있으며, 재고 항목은 포함되지 않습니다.
요청
urlOrId| 이름 | 안 | 유형 | 필수 | 설명 |
|---|---|---|---|---|
urlOrId | path | string | 필수 | Offer.url slug or Offer.id. |
/api/v1/offers/:urlOrId안전한 하위 집합의 목록 필드를 패치합니다. 아웃바운드 웹훅이 구성되면 offer.updated를 방출합니다.
요청
urlOrIdtitledescriptionvisibilitycategoryIdofferingIdthumbnailofferTypelistingMode| 이름 | 안 | 유형 | 필수 | 설명 |
|---|---|---|---|---|
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). |
/api/v1/offers/:urlOrId판매자 UI와 동일한 삭제/보관 규칙입니다.
요청
urlOrId| 이름 | 안 | 유형 | 필수 | 설명 |
|---|---|---|---|---|
urlOrId | path | string | 필수 | Offer.url slug or Offer.id. |
/api/v1/offers/:urlOrId/publish초안을 게시(또는 가시성을 변경)합니다. 필수 목록 필드가 불완전하면 400 오류가 발생합니다.
요청
urlOrIdvisibility| 이름 | 안 | 유형 | 필수 | 설명 |
|---|---|---|---|---|
urlOrId | path | string | 필수 | Offer.url slug or Offer.id. |
visibility | body | string | 선택 사항 | Optional. PUBLIC (default), PRIVATE, or UNPUBLISHED. |
티어별 구매 가능한 수량을 확인하고, 오른쪽 목록에 맞는 배달 필드 이름을 매칭한 후, 수량 또는 저장된 키와 계정을 보충하세요.
매칭 작동 방식
GET /api/v1/stock?fields=username,password는 해당 필드를 가진 목록을 찾습니다. 옵션 이름(또는 optionId)과 필드 이름으로 재고를 보충하세요. 내부 필드 ID는 필요하지 않습니다. 응답에는 절대 자격 증명 값이 포함되지 않습니다.
/api/v1/stock티어별 수량과 배달 필드 이름을 반환하여 키와 계정을 올바른 오퍼에 매칭할 수 있습니다. q, fields, stockMode 및 lowStock으로 필터링하세요. 절대 자격 증명 값을 반환하지 않습니다.
요청
qfieldsstockModelowStockarchive| 이름 | 안 | 유형 | 필수 | 제한 | 설명 |
|---|---|---|---|---|---|
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". |
/api/v1/offers/:urlOrId/stock하나의 URL 또는 숫자 ID에 대한 StockOffer 형태와 동일합니다. 수량만 포함됩니다.
요청
urlOrId| 이름 | 안 | 유형 | 필수 | 설명 |
|---|---|---|---|---|
urlOrId | path | string | 필수 | Offer.url slug or Offer.id. |
/api/v1/offers/:urlOrId/stock수량 티어: 추가, 제거 또는 설정. 저장된 항목 티어: 필드 이름별 항목 객체, 하나의 필드가 있을 때 keys[], 또는 구분된 텍스트. 여러 티어를 하나의 호출로 options[]를 통해 전달합니다. dryRun은 매칭 미리보기를 제공합니다. onDuplicate는 기본적으로 건너뛰기로 설정됩니다.
{
"option": "1 Month",
"add": 50
}요청
urlOrIdoptionoptionIdaddremovesetitemskeystextdelimiterheadersoptionsdryRunonDuplicate| 이름 | 안 | 유형 | 필수 | 제한 | 설명 |
|---|---|---|---|---|---|
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. |
계정에는 items[], 단일 필드 라이센스 코드에는 keys[]를 사용하여 POST /api/v1/offers/:url/stock를 사용하세요. 요청당 1,000행으로 나누어 보내세요.
리스트와 일치하는 페이로드 선택
먼저 GET stock을 호출하세요. fields[]에 이름이 여러 개 있으면 해당 이름으로 키가 지정된 items 객체를 보내세요 (사용자 이름, 비밀번호, 이메일). 필드가 정확히 하나인 경우 keys[]만으로 충분합니다. 수량 목록은 items가 아닌 add를 사용합니다.
요청당 1,000행. 티어당 5,000개의 미판매 아이템. 분당 300개의 요청. 기본적으로 중복은 건너뜁니다.
accounts.json (계정당 하나의 객체)
[
{ "Username": "player1", "Password": "secret1", "E-Mail": "[email protected]" },
{ "Username": "player2", "Password": "secret2", "E-Mail": "[email protected]" }
]accounts.csv
Username,Password,E-Mail
player1,secret1,p1@example.com
player2,secret2,p2@example.comkeys.txt (한 줄에 하나의 라이센스 키)
AAAA-BBBB-CCCC
DDDD-EEEE-FFFF
GGGG-HHHH-IIIIcURL
# 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행)
// 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 별칭이 아님). PowerShell이 JSON을 분할하지 않도록 -d JSON을 따옴표로 묶으세요.
주문은 판매자 계정에 한정됩니다. 구매자 청구 세부정보는 기록된 마켓플레이스 개인 정보 보호 규칙에 따라 수정될 수 있습니다.
/api/v1/orderslimit, offset, status, q 및 sort(최신, 오래된, total_high, total_low)를 지원합니다.
요청
limitoffsetstatusqsort| 이름 | 안 | 유형 | 필수 | 제한 | 설명 |
|---|---|---|---|---|---|
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. |
/api/v1/orders/:uid라인 항목이 포함된 주문을 반환합니다. 공개 주문 uid를 사용하십시오.
요청
uid| 이름 | 안 | 유형 | 필수 | 설명 |
|---|---|---|---|---|
uid | path | string | 필수 | Order.uid. |
/api/v1/orders/:uid/deliver수동 이행. COMPLEX 라인은 필수일 때 완전히 첨부되어야 합니다. order.delivered를 방출합니다.
요청
uidevidence| 이름 | 안 | 유형 | 필수 | 제한 | 설명 |
|---|---|---|---|---|---|
uid | path | string | 필수 | Order.uid. | |
evidence | body | string[] | 선택 사항 | HTTPS, max 10 | Optional screenshot or transfer-proof URLs. |
승인된 파트너 상점이나 백엔드는 구매자를 RMT.GG 결제 페이지로 보낼 수 있습니다. 우리는 기록된 상인이며 잠금 금액의 4%를 차지합니다.
허용 목록 및 이행
설정에서 호스팅된 체크아웃을 적용한 후, API 키와 JSON 웹훅을 생성하세요. 결제 후 checkout.completed 이벤트가 발생합니다. 배송 값은 RMT.GG 확인서에만 있으며, 판매자 GET 또는 웹훅에는 포함되지 않습니다.
/api/v1/checkout/sessions구매자를 잠긴 RMT.GG 결제 페이지로 보냅니다. 하나의 아이템: 금액과 itemName. 장바구니: 각 줄에 이름과 금액이 있는 items[]. 통화는 기본적으로 USD입니다. 결제 후, 배송 정보가 복사될 때까지 구매자는 RMT.GG에 남아 있습니다. returnUrl은 상점으로 계속 이동합니다; 배송이 없으면 짧은 카운트다운 후에 다시 보내드립니다. 금액, 길이 및 기타 제한은 Limits 열에 있습니다.
{
amount: 10, // what the buyer pays
itemName: "Gold pack", // pay page heading
}요청
amountcurrencyitemNametitledescriptionimageUrlitemsitems[].nameitems[].titleitems[].descriptionitems[].amountitems[].quantityitems[].imageUrlitems[].deliveryitems[].delivery[].nameitems[].delivery[].typeitems[].delivery[].valueemailreturnUrlcancelUrlinvoiceIdcategorySlugofferingmetadataIdempotency-Key| 이름 | 안 | 유형 | 필수 | 제한 | 설명 |
|---|---|---|---|---|---|
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. |
응답
uidstatuspaidamountcurrencyitemNamedescriptionemaillangreturnUrlcancelUrlinvoiceIdexternalInvoiceIdsourceexpiresAthostedUrlorderUiditemshosted_urlexpires_at| 이름 | 안 | 유형 | 설명 |
|---|---|---|---|
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. |
/api/v1/checkout/sessions/:uid생성한 세션을 반환합니다. checkout.completed가 지연될 경우 이 값을 사용하세요. paid는 상태가 paid일 때만 true입니다. items에는 배송 값이 포함되지 않습니다.
요청
uid| 이름 | 안 | 유형 | 필수 | 설명 |
|---|---|---|---|---|
uid | path | string | 필수 | Session uid returned at create time. |
응답
uidstatuspaidamountcurrencyitemNamedescriptionemaillangreturnUrlcancelUrlinvoiceIdexternalInvoiceIdsourceexpiresAthostedUrlorderUiditems| 이름 | 안 | 유형 | 설명 |
|---|---|---|---|
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. |
/api/v1/checkout/sessionsuid로 GET한 것과 동일한 세션 객체입니다. 생성 시 보낸 invoiceId를 전달하세요. 누락: 400 INVOICE_ID_REQUIRED. 알 수 없음: 404 NOT_FOUND.
요청
invoiceId| 이름 | 안 | 유형 | 필수 | 제한 | 설명 |
|---|---|---|---|---|---|
invoiceId | query | string | 필수 | Max 128 | invoiceId from create. Missing: 400 INVOICE_ID_REQUIRED. Too long: 400 INVALID_INVOICE_ID. |
응답
uidstatuspaidamountcurrencyitemNamedescriptionemaillangreturnUrlcancelUrlinvoiceIdexternalInvoiceIdsourceexpiresAthostedUrlorderUiditems| 이름 | 안 | 유형 | 설명 |
|---|---|---|---|
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. |
설정 → 개발자에서 HTTPS 엔드포인트(또는 Discord 웹훅)를 구성합니다. 구독된 이벤트가 발생하면 RMT가 POST합니다.
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 */ ]
}
}
}서명된 배송 헤더
{
"X-RMT-Event": "order.paid",
"X-RMT-Delivery": "whd_…",
"X-RMT-Timestamp": "1710000000",
"X-RMT-Signature": "v1=abc123…"
}체크아웃 완료 페이로드
{
"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 페이로드
{
"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"
}
]
}
}
}서명 비밀이 설정되면 timestamp + '.' + rawBody에 대해 HMAC-SHA256을 계산하고 v1= 이후의 16진수와 비교합니다.
비밀 키는 RMT에 유지됩니다. 각 서명된 POST에는 X-RMT-Timestamp (유닉스 초)와 X-RMT-Signature (v1= 및 헥스)가 포함됩니다. 비밀 키를 사용하여 문자열 timestamp + '.' + rawBody에 대해 HMAC-SHA256을 계산한 후 v1= 뒤의 헥스와 비교합니다. 5분 이상 된 타임스탬프는 거부합니다.
타입스크립트 검증 (타이밍 안전 비교 및 5분 재생 창)
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();
타입스크립트 웹훅 핸들러
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;
}
}
COMPLEX(고유 단위) 목록의 경우, RMT는 결제 후 로컬 재고가 부족할 때 다음 라이센스, 계정 또는 키를 발행하기 위해 HTTPS 엔드포인트에 POST할 수 있습니다.
결제 안전 실패
엔드포인트가 시간 초과되거나 잘못된 데이터를 반환하면 주문은 PAID 상태로 유지됩니다. 구매자는 요금이 청구되며, 주문에서 오류를 보고 수동으로 예약하거나 키를 첨부할 수 있습니다.
정규 POST 본문(잘림)
{
"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
}요청 헤더 (서명 비밀이 설정된 경우)
{
"Content-Type": "application/json",
"X-RMT-Event": "reserve.item",
"X-RMT-Delivery": "rsv_…",
"X-RMT-Timestamp": "1710000000",
"X-RMT-Signature": "v1=abc123…"
}편의 응답
{
"entries": [
{ "name": "License", "value": "AAAA-BBBB-CCCC" }
]
}매핑된 JSON 필드(응답 맵 경로는 $.license와 같은 형식)
{
"license": "AAAA-BBBB-CCCC",
"email": "[email protected]",
"password": "temporary-pass"
}오퍼에 비밀을 설정하면 모든 reserve POST가 서명됩니다. HMAC-SHA256(secret, timestamp + '.' + rawBody)를 재계산하고 v1= 접두사를 제거한 후 X-RMT-Signature와 비교하세요. 비밀 자체는 요청에 포함되지 않습니다.
전체 검증 예제를 확인하세요타입스크립트 예약 핸들러 (검증 후 항목 반환)
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_REQUIREDAuthorization 또는 X-Api-Key 헤더가 누락되었습니다.
API_KEY_INVALID키가 알 수 없거나, 취소되었거나, 만료되었거나, 개발자 접근이 중단되었습니다.
SCOPE_MISSING키에 엔드포인트에서 요구하는 범위가 없습니다.
RATE_LIMITED요청이 너무 많습니다. Retry-After 및 X-RateLimit-Reset을 준수하십시오.
CHECKOUT_PARTNER_NOT_APPROVED이 판매자는 호스팅 체크아웃에 승인되지 않았습니다.
INVALID_JSON요청 본문은 JSON이어야 합니다.
INVALID_AMOUNT금액은 0보다 커야 하며 최대 1,000,000이어야 합니다.
UNSUPPORTED_CURRENCY통화는 지원되는 ISO 코드가 아닙니다.
INVALID_RETURN_URLreturnUrl 및 cancelUrl은 https여야 합니다 (http://localhost는 로컬 상점에 허용됨).
INVALID_PSP_CATEGORYcategorySlug와 offering은 함께 전송되어야 하며 카탈로그 쌍과 일치해야 합니다.
INVALID_INVOICE_IDinvoiceId는 128자보다 깁니다.
INVOICE_ID_REQUIREDGET /checkout/sessions는 쿼리 매개변수로 invoiceId가 필요합니다.
INVALID_IDEMPOTENCY_KEYIdempotency-Key는 128자보다 깁니다.
INVALID_METADATAmetadata는 배열이나 원시 값이 아닌 JSON 객체여야 합니다.
METADATA_TOO_LARGE직렬화된 메타데이터는 4096자보다 큽니다.
IDEMPOTENCY_CONFLICTIdempotency-Key가 다른 금액, 통화 또는 항목과 함께 재사용되었습니다.
INVOICE_CONFLICTinvoiceId가 다른 금액, 통화 또는 항목과 함께 재사용되었습니다.
INVALID_IMAGE_URLimageUrl은 https URL이어야 합니다.
ITEM_NAME_REQUIRED아이템 이름(itemName) 또는 제목(title)은 아이템이 생략될 경우 필수입니다.
INVALID_ITEMSitems는 비어 있지 않은 잠긴 항목 배열이어야 합니다(최대 20개). 각 항목은 이름과 금액이 필요합니다.
TOO_MANY_ITEMSitems는 20개 이상의 항목을 포함할 수 없습니다.
AMOUNT_MISMATCHamount는 각 항목의 금액과 수량의 곱의 합과 같아야 합니다.
INVALID_DELIVERY배송 필드가 유효하지 않습니다. 각 필드는 이름(최대 80)과 값(최대 2048)이 필요합니다. type은 text, password 또는 textarea여야 합니다(기본값은 text). 각 줄당 최대 16개 필드.
ITEMS_TOO_LARGE직렬화된 아이템 JSON이 48,000자를 초과합니다.
NOT_FOUND해당 uid 또는 invoiceId에 맞는 호스팅된 체크아웃 세션이 없습니다.
RESERVE_FAILED예약 웹훅이 시간 초과되었거나 잘못된 데이터를 반환했거나 필수 필드를 놓쳤습니다.
OPTION_AMBIGUOUS해당 이름과 일치하는 가격 옵션이 둘 이상입니다. GET stock에서 optionId를 전달하세요.
OPTION_NOT_FOUND이 목록에서 해당 ID 또는 이름과 일치하는 가격 옵션이 없습니다.
OPTION_REQUIRED이 목록에는 여러 가격 옵션이 있습니다. option 또는 optionId를 전달하세요.
UNKNOWN_FIELD필드 이름이 이 목록의 배달 스키마와 일치하지 않습니다.
FIELD_MAPPING_AMBIGUOUS열 또는 키를 배달 필드에 매핑할 수 없습니다. 헤더를 보내거나 필드 이름으로 키가 지정된 항목 객체를 사용하세요.
STOCK_MODE_MISMATCH해당 페이로드가 옵션의 재고 모드(수량 vs 저장된 항목)와 일치하지 않습니다.
IMPORT_TOO_LARGE재고 보충 요청은 옵션당 최대 1,000개의 저장된 항목을 가져올 수 있습니다.
OPTION_ITEM_CAPACITY이 가격 옵션은 이미 최대 5,000개의 판매되지 않은 저장된 항목을 보유하고 있습니다.
DUPLICATE_ITEMSonDuplicate=error이며 이 옵션에 이미 하나 이상의 항목이 존재합니다.
UNLIMITED_STOCK이 옵션은 무제한 수량입니다. 먼저 유한 수로 전환하려면 set을 사용하세요.
INSUFFICIENT_STOCK제거할 수량 재고가 충분하지 않습니다.
STOCK_HELD_IN_CHECKOUT체크아웃에서 예약된 수량보다 수량을 낮출 수 없습니다.
INVALID_RESTOCK재고 보충 본문에 필수 작업이 누락되었거나 하나의 옵션에서 add/items가 결합되어 있습니다.
| 코드 | HTTP | 설명 |
|---|---|---|
| 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 | 직렬화된 메타데이터는 4096자보다 큽니다. |
| IDEMPOTENCY_CONFLICT | 409 | Idempotency-Key가 다른 금액, 통화 또는 항목과 함께 재사용되었습니다. |
| INVOICE_CONFLICT | 409 | invoiceId가 다른 금액, 통화 또는 항목과 함께 재사용되었습니다. |
| INVALID_IMAGE_URL | 400 | imageUrl은 https URL이어야 합니다. |
| ITEM_NAME_REQUIRED | 400 | 아이템 이름(itemName) 또는 제목(title)은 아이템이 생략될 경우 필수입니다. |
| INVALID_ITEMS | 400 | items는 비어 있지 않은 잠긴 항목 배열이어야 합니다(최대 20개). 각 항목은 이름과 금액이 필요합니다. |
| TOO_MANY_ITEMS | 400 | items는 20개 이상의 항목을 포함할 수 없습니다. |
| AMOUNT_MISMATCH | 400 | amount는 각 항목의 금액과 수량의 곱의 합과 같아야 합니다. |
| INVALID_DELIVERY | 400 | 배송 필드가 유효하지 않습니다. 각 필드는 이름(최대 80)과 값(최대 2048)이 필요합니다. type은 text, password 또는 textarea여야 합니다(기본값은 text). 각 줄당 최대 16개 필드. |
| ITEMS_TOO_LARGE | 400 | 직렬화된 아이템 JSON이 48,000자를 초과합니다. |
| NOT_FOUND | 404 | 해당 uid 또는 invoiceId에 맞는 호스팅된 체크아웃 세션이 없습니다. |
| RESERVE_FAILED | 400 | 예약 웹훅이 시간 초과되었거나 잘못된 데이터를 반환했거나 필수 필드를 놓쳤습니다. |
| 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 | 해당 페이로드가 옵션의 재고 모드(수량 vs 저장된 항목)와 일치하지 않습니다. |
| IMPORT_TOO_LARGE | 400 | 재고 보충 요청은 옵션당 최대 1,000개의 저장된 항목을 가져올 수 있습니다. |
| OPTION_ITEM_CAPACITY | 400 | 이 가격 옵션은 이미 최대 5,000개의 판매되지 않은 저장된 항목을 보유하고 있습니다. |
| DUPLICATE_ITEMS | 409 | onDuplicate=error이며 이 옵션에 이미 하나 이상의 항목이 존재합니다. |
| UNLIMITED_STOCK | 400 | 이 옵션은 무제한 수량입니다. 먼저 유한 수로 전환하려면 set을 사용하세요. |
| INSUFFICIENT_STOCK | 400 | 제거할 수량 재고가 충분하지 않습니다. |
| STOCK_HELD_IN_CHECKOUT | 400 | 체크아웃에서 예약된 수량보다 수량을 낮출 수 없습니다. |
| INVALID_RESTOCK | 400 | 재고 보충 본문에 필수 작업이 누락되었거나 하나의 옵션에서 add/items가 결합되어 있습니다. |
429 처리
Retry-After 초를 사용하여 대기하십시오. 제한을 우회하기 위해 키를 회전하지 마십시오; 제한은 키당 적용되며 모든 판매자에게 동일합니다.
성공적인 응답에는 X-RateLimit-Limit, X-RateLimit-Remaining 및 X-RateLimit-Reset이 포함됩니다.