विषय पर बढ़ें

Brand Partner API

mintbot orders बनाने, उनका status poll करने, और lifecycle events receive करने के लिए एक छोटा, predictable REST API। JSON in, JSON out। Bearer-token auth। Idempotent writes। Signed webhooks।

API access dashboard Webhooks पर जाएँ


एक नज़र में

Base URL https://mint.mintbot.ai/api/v1
Auth Authorization: Bearer mo_live_…
Idempotency POST /dns/records को छोड़कर हर POST पर Idempotency-Key: <uuid>
Content type application/json
Rate limit 120 req / 60 s per partner
Webhooks HMAC-SHA256 से signed, 7 बार तक retry

Authentication

हर request Authorization header में partner API key भेजती है। Key dashboard में generate या rotate करें।

Authorization: Bearer mo_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

Rotation में grace window नहीं है

Key rotate करने से previous key atomically revoke हो जाती है। Rotate पर click करने से पहले अपने config में value swap करने की planning करें।

Idempotency

हर POST के लिए Idempotency-Key header चाहिए — सिवाय POST /dns/records के, जो provider layer पर स्वाभाविक रूप से idempotent है और इस header के बिना भी retries स्वीकार करता है। अलग-अलग request के लिए कोई भी UUID चलेगा।

  • हम response को 24 hours तक cache करते हैं। Same key के साथ retry original response को Idempotent-Replay: true के साथ replay करता है।
  • Same key को different body के साथ reuse करने पर 409 idempotency_key_mismatch return होता है।

Orders

Order बनाएँ

POST /orders

Order बनाता है और Stripe Checkout URL return करता है। Payment confirm होते ही mintbot agent provision करता है और partner-cut revenue event automatically record हो जाता है।

Request

{
  "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
Server lifetime के calendar months। 1, 3 या 12 में से एक होना चाहिए। आप कुछ भी भेजें, mo_test हमेशा एक 24-घंटे का run होता है।
credit_usd
वैकल्पिक, default 0। 0, 5, 10, 20, 50 में से एक। Order के साथ bundled pre-funded chat credit (USD)। यह order total में जुड़ता है और mintbot को जाता है — यह आपके margin का हिस्सा नहीं है।
language
वैकल्पिक। Stripe Checkout locale और welcome-email language को affect करता है।
external_id
वैकल्पिक। हर webhook और order response में वापस echo होता है — इसका इस्तेमाल mintbot orders को अपने system की rows से जोड़ने के लिए करें।
success_url · cancel_url
Stripe Checkout return URLs। {ORDER_ID} server-side substitute होता है।
webhook_url
वैकल्पिक। Partner-level webhook URL का per-order override।
product_name
वैकल्पिक। Stripe Checkout पर customer को दिखने वाला line-item label (अधिकतम 250 characters)। Default में TIER · N months जैसा label generate होता है।
price_usd
वैकल्पिक। पूरे order के लिए USD में price override। यह उस tier और duration के आपके wholesale floor से नीचे नहीं जा सकता — वरना 400 validation_error।

Response — 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
}

अगर आप custom pricing mode में हैं और इस tier की कीमत तय नहीं की है, तो response में "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"]

Order लें

GET /orders/{id}

एक single order को उसके mintbot id से fetch करता है। POST /orders जैसी same shape return करता है।

curl https://mint.mintbot.ai/api/v1/orders/42 \
  -H "Authorization: Bearer $MINTBOT_API_KEY"

Orders list करें

GET /orders

Cursor-paginated list, newest first।

Query parameters

status
वैकल्पिक। pending, completed या expired से filter करें। Paid order पूरे deploy के दौरान completed ही रहता है — deploy की progress agent.* webhooks से track करें।
cursor
वैकल्पिक। Continue करने के लिए previous page से next_cursor pass करें।
limit
वैकल्पिक। Page size, default 50, अधिकतम 200।

Response

{
  "items": [ /* OrderResponse … */ ],
  "next_cursor": "37"
}

जब और pages नहीं होते, next_cursor null होता है।


Order renew करें

POST /orders/{id}/renew

Existing agent को another duration_months के लिए extend करता है। Infra-only — fresh chat credit bundled नहीं होता। New order id और fresh Stripe Checkout URL return करता है।

Request

{
  "duration_months": 1,
  "external_id": "your-side-id",
  "success_url": "https://your.app/thanks?id={ORDER_ID}",
  "cancel_url":  "https://your.app/account"
}

Subscriptions

Subscription बनाएँ

POST /subscriptions

Subscription mode में एक Stripe Checkout खोलता है: customer एक monthly package के लिए हर महीने pay करता है और subscription अपने आप renew होता है। POST /orders की तरह इसके लिए भी Idempotency-Key और live partner account चाहिए। मासिक कीमत उस tier के लिए आपकी pricing से आती है।

Request

{
  "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 (आपका कोई मौजूदा agent, जिससे subscription जोड़ना है), external_id और product_name वैकल्पिक हैं।

Response — 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 होते हैं।

Subscription लें

GET /subscriptions/{id}

POST /subscriptions जैसा ही shape लौटाता है।


Revenue

Revenue पढ़ें

GET /revenue

Totals plus latest 200 ledger events।

Query parameters

include_paid
वैकल्पिक, default true। सिर्फ वे events देखने के लिए false set करें जिनका payout अभी नहीं हुआ है।

Response

{
  "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
    }
  ]
}

Partner profile

Profile लें

GET /partner

आपकी partner profile और unpaid balance echo करता है — अपने admin UI के अंदर earnings दिखाने के लिए handy है, उन्हें खुद store किए बिना।

Response

{
  "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"
}

Settings

Settings लें

GET /settings

आपकी पूरी non-secret partner configuration एक ही payload में लौटाता है — चुने गए modes, apex domain, DNS provider, bot provider, customization repo, subdomain pattern (rendered preview label के साथ), हर tier की pricing, API key metadata, webhook metadata, और वही readiness / section-wise status जिससे MintOffice dashboard का banner चलता है।

यह उन integration agents के लिए बनाया गया है जिन्हें dashboard scrape किए बिना या operator से पूछे बिना जानना होता है कि partner कैसे set up है। आम caller एक coding agent होता है जो नए server पर आपका white-label setup install कर रहा है — वह यह endpoint पढ़ सकता है, readiness.status के आधार पर आगे बढ़ सकता है, और operator को ठीक-ठीक बता सकता है कि किन sections पर अभी ध्यान देना बाकी है।

कोई secret कभी नहीं लौटाया जाता

Partner ने credential के रूप में जो कुछ दिया है — Telegram bot token, Zone.ee API key, webhook signing secret, खुद API key का plaintext — वह सिर्फ एक *_set boolean के रूप में दिखता है। Webhook secret के लिए वही 8-character display prefix भी आता है जो dashboard में पहले से दिखता है, ताकि caller value देखे बिना पक्का कर सके कि कौन-सा secret configured है।

Response

{
  "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
  }
}

Field reference

status
Partner lifecycle। onboarding, live, suspended में से एक। Status live न हो तो Brand Partner API writes reject करता है; आपको live पर सिर्फ mintbot team ले जा सकती है।
domain.mode
own (आप अपना apex domain लाते हैं) या mintbot (mintbot एक *.mintbot.ai subdomain देता है)। mintbot होने पर partner ने पहले जो भी डाला हो, DNS-provider fields जानबूझकर खाली रहते हैं।
domain.subdomain_label_preview
मौजूदा subdomain_prefix, subdomain_numbering और subdomain_pad_width के हिसाब से पहले customer agent को मिलने वाला rendered label (agent1, 1, 001, …)। Numbering rules खुद दोबारा implement किए बिना "आपके customers यह देखेंगे" वाला preview दिखाने के लिए इसका इस्तेमाल करें।
bot.mode
own (partner का अपना Telegram bot), mintbot (testing के दौरान mintbot का bot), या none (सिर्फ panel, Telegram नहीं)।
template.mode
default (कोई agent customization repo नहीं — agent सादे, बिना brand वाले base पर चलता है) या own (repo_url पर partner का agent customization repo)। own होने पर standard deploy के बाद agent का अपना VPS वह repo clone करता है और उसका install.sh / update.sh चलाकर base के ऊपर आपकी persona, panel theme और कोई भी extra tooling लगाता है — central कभी खुद repo नहीं चलाता। Custom branding चाहिए तो repo_url को अपने GitHub HTTPS agent customization repo पर point करें।
api.webhook_secret_prefix
Webhook signing secret के पहले 8 characters; सिर्फ तब मौजूद जब secret configured हो। इससे caller पूरी value देखे बिना पहचान सकता है कि कौन-सा secret लगा है। Dashboard भी यही prefix दिखाता है।
pricing.allowed_credit_options
आप अपने customers को जो credit_usd bundles offer करते हैं (Settings → Pricing में चुने गए)।
readiness.missing_fields
Go Live से पहले अभी भी ज़रूरी dashboard field names की list। ready के true होने पर खाली। आपके अपने admin में "अभी क्या बाकी है" UI चलाने लायक stable।
readiness.dns_blocker
DNS provider check fail होने पर इंसान के पढ़ने लायक कारण (जैसे partner has no zone.ee username configured)। वरना null।
section_status.*
Dashboard के section-wise status pills को दर्शाता है। status ok, warn या default (mintbot का default इस्तेमाल हो रहा है) होता है; issue उस field का नाम बताता है जो उस section को रोक रहा है (जैसे 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"])

Catalog

आपके storefront के लिए बेचे जा सकने वाले packages की list — Brand Partner packages (mo_test / mo_month / mo_month_plus), आपकी resolved pricing के साथ, आपकी pricing currency में। Tier slugs और कीमतें hard-code करने के बजाय plan cards इसी से render करें: बंद किए गए packages अपने आप गायब हो जाते हैं, और dashboard में आपका किया कीमत या currency का बदलाव cache window के भीतर storefront तक पहुँच जाता है — redeploy की ज़रूरत नहीं।

यही endpoint storefront को पुराने packages का प्रचार करने से रोकता है। Reference portal हर landing / /buy / /extend render पर इसे call करता है (cached, sticky fallback के साथ)।

Display copy आपके हाथ में रहती है

display_name / description / featured mintbot का canonical package metadata हैं। अपने brand के लिए किसी package का नाम बदलना या उसका विवरण दोबारा लिखना हो तो अपने storefront में override करें (reference portal में ठीक इसी के लिए PLAN_OVERRIDES map है) — catalog इस बात का source of truth है कि कौन-से packages मौजूद हैं और उनकी कीमत क्या है, आपकी marketing copy का नहीं।

Catalog लें

GET /api/v1/catalog

कोई parameter नहीं। सिर्फ Bearer auth — कोई activation gate नहीं, इसलिए आपका partner account live होने से पहले भी storefront catalog render कर सकता है।

Response

{
  "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 }
  ]
}
Field मतलब
currency आपकी pricing currency। नीचे का हर price_cents इसी currency में है।
packages[] हर public package के लिए एक entry, offer/display क्रम में।
tier वह slug जो आप POST /orders / POST /subscriptions को भेजते हैं।
display_name / description / featured Canonical display copy (चाहें तो storefront में override करें)।
default_credit_usd Package के साथ मिलने वाले LLM credit का संकेत (सिर्फ जानकारी के लिए)।
durations[] वे setup durations जिनके लिए यह package खरीदा जा सकता है। months वह value है जो आप duration_months के रूप में भेजते हैं; price_cents वह है जो order bill करेगा। Monthly packages 1 / 3 / 12 महीने दिखाते हैं; mo_test सिर्फ एक 24-घंटे की duration दिखाता है।
subscription POST /subscriptions से monthly auto-renew के रूप में खरीदे जा सकने वाले packages के लिए available true होता है; price_cents मासिक रकम है। mo_test कभी subscription के रूप में नहीं मिलता।
credit_options[] आपकी currency में कीमत वाला हर credit_usd bundle (usd = credit की रकम, price_cents = order total में जुड़ने वाली रकम)। आप कौन-से bundles offer करते हैं, यह GET /settings के pricing.allowed_credit_options से तय होता है।

कीमतें असली bill से मेल खाती हैं

हर price_cents उसी pricing resolver से निकाला जाता है जिसे order endpoint इस्तेमाल करता है, इसलिए card की कीमत हमेशा उस (tier, duration_months) के अंतिम Stripe Checkout total के बराबर होती है।

उदाहरण

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 records

Brand Partner integrations को अक्सर partner के apex domain पर DNS records बनाने पड़ते हैं — @, www, या हर customer के subdomains को सही server पर point करने के लिए। MintOffice एक vendor-निरपेक्ष proxy देता है, ताकि आप यह काम अपने infrastructure में upstream provider की API key को कभी छुए बिना कर सकें। Onboarding के दौरान save किए गए credentials (अभी zone.ee; cloudflare और route53 उसी shape में roadmap पर हैं) MintOffice में encrypted रहते हैं; आपकी call Bearer mo_live_… के साथ आती है और upstream call हम आपकी ओर से करते हैं।

Scope partner का अपना configured apex है (GET /settings का domain.client_apex_domain)। जो zone आपका नहीं है, उसके किसी record को आप छू नहीं सकते।

अगर आपका DNS provider Manual है, तो proxy कुछ नहीं बदलता: writes का कोई असर नहीं होता और lists खाली लौटती हैं।

अभी proxy वही operations support करता है जो deploy pipeline खुद इस्तेमाल करती है — A-records (upsert / list / id से delete)। Underlying provider abstraction में आने पर CNAME / TXT / MX support भी इसी shape में आएगा।

यहाँ Idempotency-Key की ज़रूरत नहीं

Order बनाने वाले endpoints के उलट, DNS upserts provider layer पर स्वाभाविक रूप से idempotent हैं (उसी (subdomain, ip) के साथ दोबारा चलाने से कुछ नहीं बदलता, और हर call पर stale duplicates साफ़ होते हैं)। Idempotency-Key header भेजने की ज़रूरत नहीं — retries हमेशा हर FQDN पर एक ही A-record पर पहुँचते हैं।

A-record upsert करें

POST /dns/records

<subdomain>.<your-apex> को idempotent तरीके से एक IPv4 address पर point करता है। Apex के लिए "" (या "@") भेजें।

Request

{
  "subdomain": "www",
  "ip": "203.0.113.10"
}

subdomain ये values लेता है:

  • "" या "@" — खुद apex (example.com)।
  • एक DNS label — "www", "api" — <label>.<apex> के लिए।
  • Dot से अलग sub-labels — "api.eu" — nested subdomains के लिए (api.eu.example.com)।

ip एक dotted IPv4 address होना चाहिए। हर label [a-z0-9_-], 1–63 characters का होता है, शुरुआत या अंत में - नहीं।

Response — 200

{
  "apex": "example.com",
  "provider": "zone_ee",
  "fqdn": "www.example.com",
  "records": [
    { "id": "9182734", "name": "www.example.com", "destination": "203.0.113.10" }
  ]
}

Write के बाद की records list हमेशा duplicate sweep के बाद FQDN के बारे में upstream provider की मौजूदा स्थिति दिखाती है — इसलिए ठीक एक record वाला सफल response इस बात का संकेत है कि कुछ भी stale नहीं बचा।

Records list करें

GET /dns/records

Configured apex के सभी A-records लौटाता है। किसी एक FQDN तक filter करने के लिए ?name=<label-or-fqdn> जोड़ें — value एक label ("www"), dotted subdomain ("api.eu"), या पूरा FQDN ("www.example.com") हो सकती है।

Response — 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" }
  ]
}

Record delete करें

DELETE /dns/records/{record_id}

record_id वह vendor identifier है जो GET /dns/records लौटाता है (zone.ee integer ids देता है, जिन्हें API boundary पर string बना दिया जाता है)। जो record अब मौजूद नहीं है उसे delete करना सफल माना जाता है — client की ओर से endpoint idempotent है।

Response — 200

{ "deleted": true, "id": "9182734" }

Errors

HTTP error.code कारण
401 unauthenticated / invalid_api_key Bearer token नहीं है या गलत है।
422 validation_error गलत subdomain या destination IPv4 नहीं है।
422 dns_not_configured Partner का client_apex_domain नहीं है या configured provider के credentials नहीं हैं। Settings → Domain में ठीक करें।
422 dns_provider_unsupported Partner का DNS provider अभी MintOffice proxy से नहीं जुड़ा है (जैसे cloudflare placeholder)। फिलहाल zone_ee पर switch करें।
502 dns_provider_error Upstream provider ने 4xx/5xx लौटाया, या transport fail हुआ। Error message में upstream status और upstream body का एक छोटा, secret हटाया हुआ हिस्सा होता है। दोबारा कोशिश करना सुरक्षित है।

उदाहरण

# 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

जब किसी order के साथ कुछ होता है, हम आपके configured webhook URL पर signed JSON event POST करते हैं।

Webhooks वैकल्पिक हैं

Settings → API access में Webhook URL और Webhook signing secret पूरी तरह वैकल्पिक हैं — अगर अभी आपके पास receiver नहीं है, तो onboarding के दौरान इन्हें खाली छोड़ दें। Deliveries तभी जाती हैं जब दोनों set हों — URL हो लेकिन signing secret न हो, तो कोई event नहीं भेजा जाता। Webhooks के बिना भी dashboard और बाकी API ठीक चलते हैं; lifecycle track करने के लिए आप GET /api/v1/orders/{id} poll कर सकते हैं। तैयार हों तब URL जोड़ें (और secret generate करें) — re-activation की ज़रूरत नहीं।

Event types

Event कब fire होता है
order.created Stripe Checkout session minted, payment का इंतज़ार।
order.paid Stripe ने payment confirm किया। Revenue event recorded।
order.cancelled Order timed out या explicitly cancelled।
agent.provisioning_started इस order के लिए deploy pipeline शुरू हुई।
agent.ready Deploy successful। Payload में panel_url और expires_at हैं।
agent.failed Deploy pipeline में error आया। error_step बताता है कि कौन-सा step टूटा; error_message एक छोटा सार है।
agent.expired Agent TTL elapsed। Partner POST /orders/{id}/renew से renew कर सकता है।
agent.resumed पहले expired हुआ agent अपनी grace window में renew हुआ और फिर से live है। agent.expired का recovery counterpart।
subscription.created · subscription.activated · subscription.updated · subscription.cancelled POST /subscriptions से बने subscription का lifecycle।
partner.activated · partner.deactivated mintbot team ने आपका account activate किया या उसे live से हटाया (payload का suspended बताता है कि account suspend हुआ या onboarding पर वापस भेजा गया)।

Delivery & retries

  • Exponential schedule पर 7 attempts तक: 0s, 30s, 2m, 10m, 1h, 6h, 24h।
  • Last attempt के बाद delivery exhausted mark हो जाती है और retry बंद हो जाता है।
  • Acknowledge करने के लिए 10 seconds के अंदर 2xx status के साथ respond करें।

Request headers

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

Sample payload — 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
}

Signature verify करना

Signature HMAC-SHA256(secret, "{timestamp}.{raw_body}") है। हमेशा raw request body verify करें — parsed JSON को re-serialise करने से comparison टूट जाएगा।

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"),
  );
}

Idempotent receivers

X-Mintbot-Event-Id को अपनी dedup key के रूप में use करें — retries same id reuse करते हैं, इसलिए उस column पर row-level INSERT … ON CONFLICT DO NOTHING आपके handler को safe रखता है।

Reference portal

चलाने लायक एक end-to-end reference mintbot-ai/partner-portal-example पर है — FastAPI + SQLite, Docker में, साथ में ExampleAI नाम का एक example brand जिसे आप fork करके अपने brand से बदलते हैं। Architecture की पूरी तस्वीर — storefront, यह API, deploy worker और customization repo कैसे साथ काम करते हैं — MintOffice — Technical पर है। Portal में ये हैं:

  • landing / plan चुनने / धन्यवाद / cancel pages,
  • POST /buy → POST /api/v1/orders → Stripe redirect,
  • ऊपर बताए HMAC verifier के साथ POST /webhooks/mintoffice,
  • HTTP Basic auth से सुरक्षित एक /admin event browser, ताकि आप deliveries पर एक नज़र डाल सकें।

इसे fork करें, .env में PARTNER_BRAND, MINTOFFICE_API_KEY और MINTOFFICE_WEBHOOK_SECRET set करें, docker compose up चलाएँ, और अपने partner account का webhook URL https://<your-host>/webhooks/mintoffice पर point करें। पूरा walkthrough MintOffice → Reference portal में देखें।


Errors

हर error response एक stable code carry करता है जिस पर आप programmatically branch कर सकते हैं। message field human-readable hint है और releases के बीच बदल सकता है। request_id, X-Request-Id response header को echo करता है — इसे support tickets में include करें।

{
  "error": {
    "code": "invalid_api_key",
    "message": "API key is unknown or revoked.",
    "request_id": "f0c2d6c4-…"
  }
}
Code Meaning
unauthenticated Missing या malformed Authorization header।
invalid_api_key API key unknown है या rotate होकर बाहर हो गई है।
rate_limited Per-partner या per-IP rate limit exceeded — Retry-After देखें।
missing_idempotency_key Idempotency-Key के बिना POST request।
idempotency_key_mismatch Same key different request body के साथ reuse हुई।
validation_error Request body schema validation fail हुआ।
not_found Resource मौजूद नहीं है या आपके partner से belong नहीं करता।
partner_not_live HTTP 403 — account के live न होते हुए एक write (POST /orders, /orders/{id}/renew, /subscriptions)। Body में status होता है, और onboarding accounts के लिए missing_fields + dns_blocker भी।
conflict HTTP 409 — उसी Idempotency-Key वाली request अभी process हो रही है। Retry-After के बाद दोबारा कोशिश करें।
unpriced_tier HTTP 409 — tier की bill करने लायक कोई कीमत नहीं है।
internal_error अप्रत्याशित server-side error। Support से संपर्क करते समय request_id शामिल करें।
payment_gateway_error Stripe ने Checkout session creation reject किया।
dns_not_configured बिना apex या बिना DNS credentials वाले partner पर POST /dns/records। Settings → Domain में ठीक करें।
dns_provider_unsupported Partner की dns_provider value अभी MintOffice proxy से नहीं जुड़ी है।
dns_provider_error Upstream DNS provider ने error लौटाया — message में secret हटाया हुआ एक हिस्सा होता है।
quota_exceeded HTTP 429, सिर्फ DEV environment (mint.mintbot.dev) में — LIVE में per-partner cap नहीं है। Partner ने सारे deploy slots इस्तेमाल कर लिए हैं (default 10, Brand Partner setup fee के बाद 20)। Cap बढ़ाने के लिए mintbot support से संपर्क करें। POST /orders और POST /orders/{id}/renew इसे लौटाते हैं। Body में deploys_used + deploy_quota होते हैं।

Rate limits

  • 120 requests / 60 s rolling window, per partner। Burst और steady-state same bucket share करते हैं।
  • 60 requests / 60 s unauthenticated और failed-auth requests के लिए separate per-IP bucket — bearer brute-force को partner quota consume करने से रोकता है।
  • Excess requests 429 rate_limited के साथ Retry-After header return करती हैं (back off करने के seconds)।

Help चाहिए?

ये docs उन partners के लिए लिखे गए हैं जो API सच में इस्तेमाल करते हैं। अगर कुछ missing, confusing, या stale है, तो अपने mintbot agent को बताएँ — वे feedback forward करेंगे और हम page update कर देंगे।