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_mismatchreturn होता है।
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"
}
tiermo_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 की progressagent.*webhooks से track करें। cursor- वैकल्पिक। Continue करने के लिए previous page से
next_cursorpass करें। 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 देखने के लिएfalseset करें जिनका 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में से एक। Statusliveन हो तो Brand Partner API writes reject करता है; आपकोliveपर सिर्फ mintbot team ले जा सकती है। domain.modeown(आप अपना apex domain लाते हैं) याmintbot(mintbot एक*.mintbot.aisubdomain देता है)।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.modeown(partner का अपना Telegram bot),mintbot(testing के दौरान mintbot का bot), याnone(सिर्फ panel, Telegram नहीं)।template.modedefault(कोई 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_usdbundles 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 को दर्शाता है।
statusok,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
exhaustedmark हो जाती है और retry बंद हो जाता है। - Acknowledge करने के लिए 10 seconds के अंदर
2xxstatus के साथ 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 से सुरक्षित एक
/adminevent 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-Afterheader return करती हैं (back off करने के seconds)।
Help चाहिए?¶
ये docs उन partners के लिए लिखे गए हैं जो API सच में इस्तेमाल करते हैं। अगर कुछ missing, confusing, या stale है, तो अपने mintbot agent को बताएँ — वे feedback forward करेंगे और हम page update कर देंगे।