コンテンツにスキップ

Brand Partner API

mintbot の注文作成、ステータス確認、ライフサイクルイベントの受信に使える、小さく予測しやすい REST API です。入力も出力も JSON。Bearer-token 認証。冪等な書き込み。署名付き Webhooks。

API アクセスダッシュボード 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"
}
tier
mo_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.mode
own(自分の apex ドメインを使用)または mintbot(mintbot が *.mintbot.ai のサブドメインを提供)。mintbot の場合、パートナーが以前に何を入力していても、DNS プロバイダーのフィールドは意図的に空になります。
domain.subdomain_label_preview
現在の subdomain_prefix、subdomain_numbering、subdomain_pad_width で 最初 の顧客エージェントに付くレンダリング済みラベル(agent1、1、001、…)です。番号付けのルールを自前で実装しなくても、「顧客にはこう表示されます」というプレビューに使えます。
bot.mode
own(パートナーが用意した Telegram ボット)、mintbot(テスト中に mintbot のボットを使用)、none(パネルのみ、Telegram なし)のいずれかです。
template.mode
default(エージェントカスタマイズリポジトリなし — エージェントはブランドなしの素のベースで動作)または 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 エージェントに伝えてください — フィードバックは転送され、ページを更新します。