Brand Partner API¶
mintbot の注文作成、ステータス確認、ライフサイクルイベントの受信に使える、小さく予測しやすい REST API です。入力も出力も JSON。Bearer-token 認証。冪等な書き込み。署名付き Webhooks。
概要¶
| Base URL | https://mint.mintbot.ai/api/v1 |
| 認証 | Authorization: Bearer mo_live_… |
| 冪等性 | POST /dns/records を除くすべての POST に Idempotency-Key: <uuid> |
| Content type | application/json |
| レート制限 | パートナーごとに 120 req / 60 s |
| Webhooks | HMAC-SHA256 で署名、最大 7 回再試行 |
認証¶
すべてのリクエストでは、Authorization ヘッダーでパートナー API キーを送信します。キーは ダッシュボード で生成またはローテーションできます。
Authorization: Bearer mo_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
ローテーションに猶予期間はありません
キーをローテーションすると、以前のキーはアトミックに失効します。Rotate をクリックする前に、設定内の値を差し替える計画を立ててください。
冪等性¶
すべての POST には Idempotency-Key ヘッダーが必要です — ただし POST /dns/records はプロバイダー層で本質的に冪等なため、このヘッダーなしでも再試行を受け付けます。個別のリクエストごとに任意の UUID を使えます。
- レスポンスは 24 時間キャッシュされます。同じキーで再試行すると、元のレスポンスが
Idempotent-Replay: true付きで再生されます。 - 同じキーを異なる body で再利用すると、
409 idempotency_key_mismatchが返ります。
注文¶
注文を作成する¶
POST /orders
注文を作成し、Stripe Checkout URL を返します。支払いが確認されると、mintbot がエージェントをプロビジョニングし、パートナー取り分の収益イベントが自動的に記録されます。
リクエスト
{
"tier": "mo_month",
"duration_months": 1,
"credit_usd": 10,
"language": "en",
"external_id": "your-side-id",
"success_url": "https://your.app/thanks?id={ORDER_ID}",
"cancel_url": "https://your.app/cart",
"webhook_url": "https://your.app/mintbot-webhook"
}
tiermo_test(Test 24h)、mo_month(Standard)、mo_month_plus(Performance)のいずれかです。duration_months- サーバーの稼働期間(暦月単位)。
1、3、12のいずれかを指定します。mo_testは、何を送っても常に 24 時間の 1 回限りです。 credit_usd- 任意、デフォルトは
0。0、5、10、20、50のいずれかです。注文にバンドルされる事前購入済みのチャットクレジット(USD)です。注文合計に加算されて mintbot に入り、あなたのマージンには含まれません。 language- 任意。 Stripe Checkout のロケールとウェルカムメールの言語に影響します。
external_id- 任意。 すべての webhook と注文レスポンスでそのまま返されます — mintbot の注文を自社システム内の行にひも付けるために使えます。
success_url·cancel_url- Stripe Checkout の戻り先 URL です。
{ORDER_ID}はサーバー側で置き換えられます。 webhook_url- 任意。 パートナーレベルの webhook URL を注文単位で上書きします。
product_name- 任意。 顧客が Stripe Checkout で目にする明細行のラベルです(最大 250 文字)。省略すると
TIER · N months形式のラベルが自動生成されます。 price_usd- 任意。 注文全体の価格を USD で上書きします。そのティアと期間の卸売下限価格を下回ることはできず、下回ると
400 validation_errorになります。
レスポンス — 201 Created
{
"id": 42,
"tier": "mo_month",
"duration_months": 1,
"credit_usd": 10,
"amount_cents": 1200,
"currency": "usd",
"status": "pending",
"checkout_url": "https://checkout.stripe.com/c/pay/cs_test_…",
"panel_url": null,
"expires_at": null,
"language": "en",
"external_id": "your-side-id",
"created_at": "2026-05-16 09:58:00",
"paid_at": null,
"price_cents": 1200,
"partner_cut_cents": 200
}
カスタム価格設定モードでこのティアの価格を設定していない場合、レスポンスには "pricing_fallback": true も含まれます。
例
curl -X POST https://mint.mintbot.ai/api/v1/orders \
-H "Authorization: Bearer $MINTBOT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"tier": "mo_month",
"duration_months": 1,
"credit_usd": 10,
"success_url": "https://your.app/thanks?id={ORDER_ID}",
"cancel_url": "https://your.app/cart"
}'
import os, uuid, requests
r = requests.post(
"https://mint.mintbot.ai/api/v1/orders",
headers={
"Authorization": f"Bearer {os.environ['MINTBOT_API_KEY']}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"tier": "mo_month",
"duration_months": 1,
"credit_usd": 10,
"success_url": "https://your.app/thanks?id={ORDER_ID}",
"cancel_url": "https://your.app/cart",
},
timeout=10,
)
r.raise_for_status()
order = r.json()
redirect_to = order["checkout_url"]
注文を取得する¶
GET /orders/{id}
mintbot id で単一の注文を取得します。POST /orders と同じ形式を返します。
curl https://mint.mintbot.ai/api/v1/orders/42 \
-H "Authorization: Bearer $MINTBOT_API_KEY"
注文を一覧する¶
GET /orders
カーソルページネーション付きの一覧です。新しいものが先に表示されます。
クエリパラメーター
status- 任意。
pending、completed、expiredで絞り込みます。支払い済みの注文はデプロイ中もずっとcompletedのままです — デプロイの進行状況はagent.*webhook で追跡してください。 cursor- 任意。 続きを取得するには、前のページの
next_cursorを渡します。 limit- 任意。 ページサイズ。デフォルトは
50、最大200です。
レスポンス
{
"items": [ /* OrderResponse … */ ],
"next_cursor": "37"
}
ページがもうない場合、next_cursor は null です。
注文を更新する¶
POST /orders/{id}/renew
既存のエージェントをさらに duration_months 延長します。インフラのみです — 新しいチャットクレジットはバンドルされません。新しい注文 id と新しい Stripe Checkout URL を返します。
リクエスト
{
"duration_months": 1,
"external_id": "your-side-id",
"success_url": "https://your.app/thanks?id={ORDER_ID}",
"cancel_url": "https://your.app/account"
}
サブスクリプション¶
サブスクリプションを作成する¶
POST /subscriptions
Stripe Checkout をサブスクリプションモードで開きます。顧客は月額パッケージを毎月支払い、サブスクリプションは自動更新されます。POST /orders と同様に、Idempotency-Key と live 状態のパートナーアカウントが必要です。月額料金は、そのティアに対するあなたの価格設定から決まります。
リクエスト
{
"tier": "mo_month",
"language": "en",
"success_url": "https://your.app/thanks",
"cancel_url": "https://your.app/cart",
"customer_email": "customer@example.com",
"external_id": "your-side-id"
}
customer_email、agent_id(サブスクリプションをひも付ける既存のエージェント)、external_id、product_name は任意です。
レスポンス — 201 Created には id、tier、currency、amount_cents、partner_cut_cents、status、checkout_url、current_period_end、cancel_at_period_end、agent_id、language、customer_email、external_id、created_at が含まれます。
サブスクリプションを取得する¶
GET /subscriptions/{id}
POST /subscriptions と同じ形式を返します。
収益¶
収益を読む¶
GET /revenue
合計と最新 200 件の ledger イベントを返します。
クエリパラメーター
include_paid- 任意、デフォルトは
true。 まだ支払われていないイベントだけを見るにはfalseに設定します。
レスポンス
{
"currency": "usd",
"gross_cents": 12000,
"partner_cut_cents": 2000,
"mintbot_cut_cents": 10000,
"unpaid_cents": 800,
"events": [
{
"id": 7,
"order_id": 42,
"kind": "order_paid",
"gross_cents": 1200,
"partner_cut_cents": 200,
"mintbot_cut_cents": 1000,
"currency": "usd",
"created_at": "2026-05-16 09:58:00",
"payout_id": null,
"payout_at": null
}
]
}
パートナープロフィール¶
プロフィールを取得する¶
GET /partner
パートナープロフィールと未払い残高を返します — 自分で保存しなくても、自社の管理 UI 内で収益を表示するのに便利です。
レスポンス
{
"id": 12,
"email": "you@kliendifirma.com",
"pricing_currency": "usd",
"balance_unpaid_cents": 800,
"webhook_url": "https://your.app/mintbot-webhook",
"api_key_prefix": "mo_live_a12b"
}
設定¶
設定を取得する¶
GET /settings
シークレット以外のパートナー設定全体を 1 つのペイロードで返します — 選択したモード、apex ドメイン、DNS プロバイダー、ボットプロバイダー、カスタマイズリポジトリ、サブドメインのパターン(レンダリング済みのプレビューラベル付き)、ティアごとの価格設定、API キーのメタデータ、webhook のメタデータ、そして MintOffice ダッシュボードのバナーを動かしているのと同じ準備状況 / セクション別ステータスです。
ダッシュボードをスクレイピングしたりオペレーターに問い合わせたりせずに、パートナーがどう設定されているか を把握する必要がある連携エージェント向けに設計されています。典型的な呼び出し元は、新しいサーバーにあなたのホワイトラベル構成をインストールするコーディングエージェントです — このエンドポイントを読み、readiness.status で分岐し、まだ対応が必要なセクションをオペレーターに正確に伝えられます。
シークレットは決して返されません
パートナーが認証情報として入力したもの — Telegram ボットトークン、Zone.ee API キー、webhook 署名シークレット、API キーの平文そのもの — は、すべて *_set の真偽値としてのみ表示されます。webhook シークレットについては、ダッシュボードにすでに表示されているのと同じ 8 文字の表示用プレフィックスも返されるため、呼び出し元は値そのものを見ずに どの シークレットが設定されているかを確認できます。
レスポンス
{
"id": 12,
"email": "you@kliendifirma.com",
"status": "live",
"created_at": "2026-05-12 14:22:01",
"onboarded_at": "2026-05-12 14:48:33",
"activated_at": "2026-05-13 09:10:05",
"last_login_at": "2026-05-20 00:51:18",
"preferred_language": "en",
"pricing_currency": "usd",
"balance_unpaid_cents": 800,
"domain": {
"mode": "own",
"client_apex_domain": "agent99.cc",
"dns_provider": "zone_ee",
"zone_ee_username": "you@kliendifirma.com",
"zone_ee_api_key_set": true,
"subdomain_prefix": "agent",
"subdomain_numbering": "text",
"subdomain_pad_width": 3,
"subdomain_label_preview": "agent1"
},
"bot": {
"mode": "own",
"provider": "telegram",
"telegram_bot_username": "agent99cc_bot",
"telegram_bot_token_set": true
},
"template": {
"mode": "default",
"repo_url": null
},
"api": {
"key_prefix": "mo_live_a12b",
"key_created_at": "2026-05-13 09:11:42",
"webhook_url": "https://your.app/mintbot-webhook",
"webhook_secret_set": true,
"webhook_secret_prefix": "ab12cd34"
},
"pricing": {
"mode": "default",
"currency": "usd",
"tiers": {
"mo_test": { "price_cents": 100, "partner_cut_cents": 0, "updated_at": "2026-05-13 09:05:00" },
"mo_month": { "price_cents": 1200, "partner_cut_cents": 200, "updated_at": "2026-05-13 09:05:00" },
"mo_month_plus": { "price_cents": 1800, "partner_cut_cents": 300, "updated_at": "2026-05-13 09:05:00" }
},
"allowed_credit_options": [10, 20, 50]
},
"readiness": {
"ready": true,
"missing_fields": [],
"dns_blocker": null,
"status": "live"
},
"section_status": {
"domain": { "status": "ok", "mode": "own", "issue": null, "dns_blocker": null },
"bot": { "status": "ok", "mode": "own", "issue": null, "dns_blocker": null },
"template": { "status": "default", "mode": "default", "issue": null, "dns_blocker": null },
"api": { "status": "ok", "mode": "active", "issue": null, "dns_blocker": null },
"all_ready": true
}
}
フィールドリファレンス¶
status- パートナーのライフサイクル。
onboarding、live、suspendedのいずれかです。ステータスがliveでない限り、Brand Partner API は書き込みを拒否します。liveに移行できるのは mintbot チームだけです。 domain.modeown(自分の apex ドメインを使用)またはmintbot(mintbot が*.mintbot.aiのサブドメインを提供)。mintbotの場合、パートナーが以前に何を入力していても、DNS プロバイダーのフィールドは意図的に空になります。domain.subdomain_label_preview- 現在の
subdomain_prefix、subdomain_numbering、subdomain_pad_widthで 最初 の顧客エージェントに付くレンダリング済みラベル(agent1、1、001、…)です。番号付けのルールを自前で実装しなくても、「顧客にはこう表示されます」というプレビューに使えます。 bot.modeown(パートナーが用意した Telegram ボット)、mintbot(テスト中に mintbot のボットを使用)、none(パネルのみ、Telegram なし)のいずれかです。template.modedefault(エージェントカスタマイズリポジトリなし — エージェントはブランドなしの素のベースで動作)またはown(repo_urlにあるパートナーの エージェントカスタマイズリポジトリ)。ownの場合、標準デプロイの後にエージェント自身の VPS がそのリポジトリを clone し、install.sh/update.shを実行して、あなたのペルソナ、パネルテーマ、追加ツールをベースの上に適用します — central がリポジトリを実行することはありません。独自ブランディングを使いたい場合は、repo_urlを GitHub HTTPS のエージェントカスタマイズリポジトリに向けてください。api.webhook_secret_prefix- webhook 署名シークレットの先頭 8 文字で、シークレットが設定されている場合にのみ含まれます。呼び出し元は値全体を見ずに どの シークレットが設定されているかを区別できます。ダッシュボードにも同じプレフィックスが表示されます。
pricing.allowed_credit_options- 顧客に提供する
credit_usdの選択肢です(Settings → Pricing で選択)。 readiness.missing_fields- Go Live の前にまだ必要なダッシュボードのフィールド名のリストです。
readyがtrueのときは空です。自社の管理画面で「まだ足りないもの」UI を作れるほど安定しています。 readiness.dns_blocker- DNS プロバイダーのチェックに失敗したときの、人が読める理由です(例:
partner has no zone.ee username configured)。それ以外はnullです。 section_status.*- ダッシュボードのセクション別ステータス表示を反映します。
statusはok、warn、default(mintbot のデフォルトを使用中)のいずれかです。issueはそのセクションをブロックしているフィールド名(例:client_apex_domain、telegram_bot_token、dns_blocker)、またはnullです。
例
curl https://mint.mintbot.ai/api/v1/settings \
-H "Authorization: Bearer $MINTBOT_API_KEY"
import os, requests
r = requests.get(
"https://mint.mintbot.ai/api/v1/settings",
headers={"Authorization": f"Bearer {os.environ['MINTBOT_API_KEY']}"},
timeout=10,
)
r.raise_for_status()
settings = r.json()
if not settings["readiness"]["ready"]:
print("Still missing:", settings["readiness"]["missing_fields"])
カタログ¶
ストアフロント用の 販売可能なパッケージ一覧 です — Brand Partner パッケージ(mo_test / mo_month / mo_month_plus)に あなたの 確定済み価格設定を組み合わせ、あなたの価格通貨で返します。ティアのスラッグや価格をハードコードせず、これを使ってプランカードを描画してください。提供終了したパッケージは自動的に消え、ダッシュボードで価格や通貨を変更すると、キャッシュ期間内にストアフロントへ反映されます — 再デプロイは不要です。
このエンドポイントがあれば、ストアフロントが古いパッケージを宣伝し続けることはありません。リファレンスポータルは、ランディング / /buy / /extend を描画するたびにこれを呼び出します(キャッシュあり、スティッキーなフォールバック付き)。
表示用の文言はあなたが決めます
display_name / description / featured は mintbot の正式なパッケージメタデータです。パッケージ名を変えたり、ブランドに合わせて説明文を書き直したりしたい場合は、自分のストアフロント側で上書きしてください(リファレンスポータルにはまさにそのための PLAN_OVERRIDES マップがあります)— カタログは どのパッケージが存在し、いくらかかるか の信頼できる情報源であり、マーケティング文言のためのものではありません。
カタログを取得する¶
GET /api/v1/catalog
パラメーターはありません。Bearer 認証のみで、有効化ゲートはありません。そのため、パートナーアカウントが live になる前でもストアフロントはカタログを描画できます。
レスポンス
{
"currency": "usd",
"packages": [
{
"tier": "mo_test",
"display_name": "Test 24h",
"description": "2 vCPU · 4 GB RAM · 40 GB SSD · 24-hour evaluation. EU.",
"featured": false,
"default_credit_usd": 5,
"durations": [
{ "months": 1, "label": "24 hours", "price_cents": 0 }
],
"subscription": { "available": false, "price_cents": null }
},
{
"tier": "mo_month",
"display_name": "Standard",
"description": "2 vCPU · 4 GB RAM · 40 GB SSD, monthly. EU.",
"featured": false,
"default_credit_usd": 10,
"durations": [
{ "months": 1, "label": "1 month", "price_cents": 1500 },
{ "months": 3, "label": "3 months", "price_cents": 4500 },
{ "months": 12, "label": "12 months", "price_cents": 18000 }
],
"subscription": { "available": true, "price_cents": 1500 }
}
],
"credit_options": [
{ "usd": 5, "price_cents": 500 },
{ "usd": 10, "price_cents": 1000 }
]
}
| フィールド | 意味 |
|---|---|
currency |
あなたの価格通貨です。以下の すべての price_cents はこの通貨建てです。 |
packages[] |
公開パッケージごとに 1 件、提供 / 表示順に並びます。 |
tier |
POST /orders / POST /subscriptions に渡すスラッグです。 |
display_name / description / featured |
正式な表示用文言です(必要ならストアフロント側で上書きしてください)。 |
default_credit_usd |
パッケージにバンドルされる LLM クレジットの目安です(参考情報)。 |
durations[] |
このパッケージを購入できるセットアップ期間です。months は duration_months として送る値、price_cents はその注文で請求される金額です。月額パッケージは 1 / 3 / 12 か月、mo_test は 24 時間の 1 期間のみです。 |
subscription |
POST /subscriptions で月次自動更新として購入できるパッケージでは available が true になり、price_cents は月額です。mo_test はサブスクリプションに対応しません。 |
credit_options[] |
あなたの通貨で価格付けされた各 credit_usd の選択肢です(usd = クレジット額、price_cents = 注文合計に加算される金額)。どの選択肢を提供するかは GET /settings の pricing.allowed_credit_options で決まります。 |
価格は実際の請求額と一致します
各 price_cents は注文エンドポイントと同じ価格リゾルバーで計算されるため、カードに表示される価格は、その (tier, duration_months) の最終的な Stripe Checkout 合計と常に一致します。
例¶
curl -s https://mint.mintbot.ai/api/v1/catalog \
-H "Authorization: Bearer $MINTBOT_API_KEY" | jq
import os, requests
r = requests.get(
"https://mint.mintbot.ai/api/v1/catalog",
headers={"Authorization": f"Bearer {os.environ['MINTBOT_API_KEY']}"},
timeout=10,
)
r.raise_for_status()
catalog = r.json()
for pkg in catalog["packages"]:
month1 = next(d for d in pkg["durations"] if d["months"] == 1)
print(pkg["display_name"], month1["price_cents"], catalog["currency"])
DNS レコード¶
Brand Partner の連携では、パートナーの apex ドメインに DNS レコードを作成する必要がよくあります — @、www、または顧客ごとのサブドメインを適切なサーバーに向けるためです。MintOffice はベンダーに依存しないプロキシを提供しているので、上流プロバイダーの API キーを自社インフラで一切扱わずに これを行えます。オンボーディング時に保存した認証情報(現在は zone.ee。cloudflare と route53 は同じ形でロードマップ上にあります)は MintOffice 内で暗号化されたまま保持されます。あなたの呼び出しは Bearer mo_live_… で届き、上流への呼び出しは私たちが代わりに行います。
対象はパートナー自身が設定した apex(GET /settings の domain.client_apex_domain)に限られます。自分が所有していないゾーンのレコードには触れられません。
DNS プロバイダーが Manual の場合、プロキシは何も変更しません: 書き込みは何も行わず、一覧は空で返ります。
現在プロキシがサポートしているのは、デプロイパイプライン自身が使う操作 — A レコード(upsert / 一覧 / id による削除)です。CNAME / TXT / MX は、基盤となるプロバイダー抽象化が対応し次第、同じ形で追加されます。
ここでは Idempotency-Key は不要です
注文作成エンドポイントとは異なり、DNS の upsert はプロバイダー層で本質的に冪等です(同じ (subdomain, ip) で再実行しても何も変わらず、古い重複は呼び出しのたびに掃除されます)。Idempotency-Key ヘッダーを送る必要は ありません — 再試行しても FQDN ごとに A レコードは必ず 1 つに収束します。
A レコードを upsert する¶
POST /dns/records
<subdomain>.<your-apex> を IPv4 アドレスに冪等に向けます。apex 自体には ""(または "@")を渡します。
リクエスト
{
"subdomain": "www",
"ip": "203.0.113.10"
}
subdomain に指定できる値:
""または"@"— apex 自体(example.com)。- 単一の DNS ラベル —
"www"、"api"—<label>.<apex>用。 - ドット区切りのサブラベル —
"api.eu"— 入れ子のサブドメイン(api.eu.example.com)用。
ip はドット区切りの IPv4 アドレスである必要があります。各ラベルは [a-z0-9_-]、1〜63 文字で、先頭と末尾に - は使えません。
レスポンス — 200
{
"apex": "example.com",
"provider": "zone_ee",
"fqdn": "www.example.com",
"records": [
{ "id": "9182734", "name": "www.example.com", "destination": "203.0.113.10" }
]
}
書き込み後の records リストは、重複の掃除を終えた後の、その FQDN に対する上流プロバイダーの現在の状態を常に反映します — そのため、レコードがちょうど 1 件の成功レスポンスは、古いものが何も残っていないことを示す正常系のシグナルです。
レコードを一覧する¶
GET /dns/records
設定済み apex 上のすべての A レコードを返します。1 つの FQDN に絞り込むには ?name=<label-or-fqdn> を付けます — 値には単独のラベル("www")、ドット区切りのサブドメイン("api.eu")、完全な FQDN("www.example.com")を使えます。
レスポンス — 200
{
"apex": "example.com",
"provider": "zone_ee",
"items": [
{ "id": "9182734", "name": "example.com", "destination": "203.0.113.10" },
{ "id": "9182735", "name": "www.example.com", "destination": "203.0.113.10" }
]
}
レコードを削除する¶
DELETE /dns/records/{record_id}
record_id は GET /dns/records が返すベンダー側の識別子です(zone.ee は整数 id を発行し、API の境界で文字列に変換されます)。すでに存在しないレコードの削除は成功として扱われます — このエンドポイントはクライアント側から見て冪等です。
レスポンス — 200
{ "deleted": true, "id": "9182734" }
エラー¶
| HTTP | error.code |
原因 |
|---|---|---|
| 401 | unauthenticated / invalid_api_key |
Bearer トークンがない、または不正です。 |
| 422 | validation_error |
サブドメインが不正、または宛先が IPv4 ではありません。 |
| 422 | dns_not_configured |
パートナーに client_apex_domain がない、または設定済みプロバイダーの認証情報がありません。Settings → Domain で修正してください。 |
| 422 | dns_provider_unsupported |
パートナーの DNS プロバイダーがまだ MintOffice プロキシに接続されていません(例: cloudflare のプレースホルダー)。当面は zone_ee に切り替えてください。 |
| 502 | dns_provider_error |
上流プロバイダーが 4xx/5xx を返したか、通信に失敗しました。エラーの message には上流のステータスと、シークレットを除去した上流 body の短い抜粋が含まれます。再試行しても安全です。 |
例¶
# Point apex + www at your server's IP.
curl https://mint.mintbot.ai/api/v1/dns/records \
-H "Authorization: Bearer $MINTBOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subdomain": "", "ip": "203.0.113.10"}'
curl https://mint.mintbot.ai/api/v1/dns/records \
-H "Authorization: Bearer $MINTBOT_API_KEY" \
-H "Content-Type: application/json" \
-d '{"subdomain": "www", "ip": "203.0.113.10"}'
# Verify.
curl "https://mint.mintbot.ai/api/v1/dns/records" \
-H "Authorization: Bearer $MINTBOT_API_KEY"
import os, requests
URL = "https://mint.mintbot.ai/api/v1/dns/records"
H = {"Authorization": f"Bearer {os.environ['MINTBOT_API_KEY']}"}
for sub in ("", "www"):
r = requests.post(URL, headers=H, json={"subdomain": sub, "ip": "203.0.113.10"})
r.raise_for_status()
print(sub or "@", r.json()["records"])
Webhooks¶
注文に何かが起きると、設定済みの webhook URL に署名付き JSON イベントを POST します。
Webhooks は任意です
Settings → API access の Webhook URL と Webhook signing secret は完全に任意です — まだ受信側がない場合は、オンボーディング中は空欄のままで構いません。配信が行われるのは 両方 が設定されてからです — URL があっても署名シークレットがなければ、イベントは送信されません。ダッシュボードと API の他の部分は webhook なしでも問題なく動作します。代わりに GET /api/v1/orders/{id} をポーリングしてライフサイクルを追跡できます。準備ができたら URL を追加(してシークレットを生成)してください。再有効化は不要です。
イベントタイプ¶
| Event | 発火するタイミング |
|---|---|
order.created |
Stripe Checkout セッションが作成され、支払い待ちになります。 |
order.paid |
Stripe が支払いを確認しました。収益イベントが記録されます。 |
order.cancelled |
注文がタイムアウトしたか、明示的にキャンセルされました。 |
agent.provisioning_started |
この注文のデプロイパイプラインが開始されました。 |
agent.ready |
デプロイに成功しました。ペイロードには panel_url と expires_at が含まれます。 |
agent.failed |
デプロイパイプラインでエラーが発生しました。error_step は失敗したステップ名、error_message は短い要約です。 |
agent.expired |
エージェントの TTL が経過しました。パートナーは POST /orders/{id}/renew で更新できます。 |
agent.resumed |
期限切れになったエージェントが猶予期間内に更新され、再び稼働しています。agent.expired に対応する復旧イベントです。 |
subscription.created · subscription.activated · subscription.updated · subscription.cancelled |
POST /subscriptions で作成したサブスクリプションのライフサイクルです。 |
partner.activated · partner.deactivated |
mintbot チームがあなたのアカウントを有効化した、または live から外しました(ペイロードの suspended で、停止されたのかオンボーディングに戻されたのかがわかります)。 |
配信と再試行¶
- 指数スケジュールで最大 7 回試行します:
0s, 30s, 2m, 10m, 1h, 6h, 24h。 - 最後の試行後、配信は
exhaustedとしてマークされ、再試行は停止します。 - 確認応答するには、10 秒以内に
2xxステータスで応答してください。
リクエストヘッダー¶
Content-Type: application/json
User-Agent: mintbot-webhook/1.0
X-Mintbot-Signature: t=<unix_ts>,v1=<hex_hmac_sha256>
X-Mintbot-Event-Id: evt_42_order.paid_1747371234567
X-Mintbot-Event-Type: order.paid
サンプルペイロード — order.paid¶
{
"id": 42,
"tier": "mo_month",
"duration_months": 1,
"currency": "usd",
"amount_cents": 1200,
"status": "completed",
"external_id": "your-side-id",
"created_at": "2026-05-16 09:58:00",
"paid_at": "2026-05-16 10:02:14",
"expires_at": null
}
署名を検証する¶
署名は HMAC-SHA256(secret, "{timestamp}.{raw_body}") です。必ず raw リクエスト body を検証してください — パースした JSON を再シリアライズすると比較が壊れます。
import hmac, hashlib, time
def verify(secret: str, body: bytes, header: str, tolerance: int = 300) -> bool:
try:
ts_part, v1_part = header.split(",", 1)
ts = int(ts_part.split("=", 1)[1])
sig = v1_part.split("=", 1)[1]
except Exception:
return False
if abs(int(time.time()) - ts) > tolerance:
return False
expected = hmac.new(
secret.encode(),
f"{ts}.".encode() + body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, sig)
const crypto = require("crypto");
function verify(secret, rawBody, header, toleranceSec = 300) {
const [tsPart, v1Part] = header.split(",");
const ts = Number(tsPart.split("=")[1]);
const sig = v1Part.split("=")[1];
if (Math.abs(Date.now() / 1000 - ts) > toleranceSec) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${ts}.`)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(sig, "hex"),
);
}
冪等な受信側
X-Mintbot-Event-Id を重複排除キーとして使ってください — 再試行では同じ id が再利用されるため、その列で行単位の INSERT … ON CONFLICT DO NOTHING を使えばハンドラーを安全に保てます。
リファレンスポータル¶
実際に動くエンドツーエンドのリファレンスが mintbot-ai/partner-portal-example にあります — FastAPI + SQLite、Docker 対応で、fork して差し替えるためのサンプルブランド ExampleAI が付属しています。ストアフロント、この API、デプロイワーカー、カスタマイズリポジトリがどう組み合わさるかというアーキテクチャの全体像は MintOffice — 技術情報 にあります。ポータルが実装しているもの:
- ランディング / プラン選択 / サンクス / キャンセルの各ページ
POST /buy→POST /api/v1/orders→ Stripe へのリダイレクト- 上で説明した HMAC 検証付きの
POST /webhooks/mintoffice - 配信をざっと確認できる、HTTP Basic 認証付きの
/adminイベントブラウザー
fork して .env に PARTNER_BRAND、MINTOFFICE_API_KEY、MINTOFFICE_WEBHOOK_SECRET を設定し、docker compose up を実行して、パートナーアカウントの webhook URL を https://<your-host>/webhooks/mintoffice に向けてください。詳しい手順は MintOffice → リファレンスポータル を参照してください。
エラー¶
すべてのエラーレスポンスには、プログラムで分岐できる安定した code が含まれます。message フィールドは人間が読めるヒントであり、リリース間で変わることがあります。request_id は X-Request-Id レスポンスヘッダーを反映します — サポートチケットに含めてください。
{
"error": {
"code": "invalid_api_key",
"message": "API key is unknown or revoked.",
"request_id": "f0c2d6c4-…"
}
}
| Code | 意味 |
|---|---|
unauthenticated |
Authorization ヘッダーがない、または形式が不正です。 |
invalid_api_key |
API key が不明、またはローテーション済みで無効です。 |
rate_limited |
パートナー単位または IP 単位のレート制限を超えました — Retry-After を参照してください。 |
missing_idempotency_key |
Idempotency-Key のない POST リクエストです。 |
idempotency_key_mismatch |
同じキーが異なるリクエスト body で再利用されました。 |
validation_error |
リクエスト body がスキーマ検証に失敗しました。 |
not_found |
リソースが存在しない、またはあなたのパートナーに属していません。 |
partner_not_live |
HTTP 403 — アカウントが live でない状態での書き込み(POST /orders、/orders/{id}/renew、/subscriptions)です。body には status が含まれ、オンボーディング中のアカウントでは missing_fields + dns_blocker も含まれます。 |
conflict |
HTTP 409 — 同じ Idempotency-Key のリクエストがまだ処理中です。Retry-After の後に再試行してください。 |
unpriced_tier |
HTTP 409 — そのティアには請求できる価格がありません。 |
internal_error |
予期しないサーバー側エラーです。サポートに連絡する際は request_id を添えてください。 |
payment_gateway_error |
Stripe が Checkout セッションの作成を拒否しました。 |
dns_not_configured |
apex または DNS 認証情報のないパートナーに対する POST /dns/records です。Settings → Domain で修正してください。 |
dns_provider_unsupported |
パートナーの dns_provider の値は、まだ MintOffice プロキシに接続されていません。 |
dns_provider_error |
上流の DNS プロバイダーがエラーを返しました — メッセージにはシークレットを除去した抜粋が含まれます。 |
quota_exceeded |
HTTP 429。DEV 環境(mint.mintbot.dev)のみです — LIVE にはパートナーごとの上限はありません。パートナーがすべてのデプロイ枠を使い切りました(デフォルトは 10、Brand Partner のセットアップ費用の支払い後は 20)。上限を引き上げるには mintbot サポートに連絡してください。POST /orders と POST /orders/{id}/renew が返します。body には deploys_used + deploy_quota が含まれます。 |
レート制限¶
- パートナーごとに 120 requests / 60 s rolling window。バーストと定常状態は同じバケットを共有します。
- 認証なしおよび認証失敗のリクエストには、IP ごとに別枠で 60 requests / 60 s。Bearer の総当たりでパートナー枠が消費されないようにします。
- 超過したリクエストには、
Retry-Afterヘッダー(バックオフする秒数)付きで429 rate_limitedが返ります。
お困りですか?¶
このドキュメントは、実際に API を使うパートナーのために書かれています。不足、わかりにくい点、古くなった内容があれば、あなたの mintbot エージェントに伝えてください — フィードバックは転送され、ページを更新します。