Brand Partner API¶
mintbot orders তৈরি করা, তাদের status poll করা, এবং lifecycle events গ্রহণ করার জন্য ছোট, 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 | প্রতি partner 120 req / 60 s |
| Webhooks | HMAC-SHA256 দিয়ে signed, সর্বোচ্চ 7 বার retry |
Authentication¶
প্রতিটি request Authorization header-এ একটি partner API key পাঠায়। dashboard-এ key generate বা rotate করুন।
Authorization: Bearer mo_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
Rotation-এর কোনো grace window নেই
Key rotate করলে আগের key atomically revoke হয়। Rotate-এ click করার আগে আপনার config-এ value swap করার পরিকল্পনা করুন।
Idempotency¶
প্রতিটি POST-এর জন্য Idempotency-Key header দরকার — শুধু POST /dns/records ছাড়া, যেটি provider layer-এ স্বাভাবিকভাবেই idempotent এবং header ছাড়াই retries গ্রহণ করে। আলাদা request প্রতি যেকোনো UUID কাজ করে।
- আমরা response 24 hours cache করি। একই key দিয়ে retry করলে original response
Idempotent-Replay: trueসহ replay হয়। - একই 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 back হয় — আপনার নিজের system-এর rows-এর সঙ্গে mintbot orders thread করতে এটি ব্যবহার করুন।
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 character)। Default হলো generated
TIER · N monthslabel। 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}
তার mintbot id দিয়ে একটি single order fetch করে। POST /orders-এর মতো একই 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-এর অগ্রগতিagent.*webhooks দিয়ে track করুন। cursor- ঐচ্ছিক। Continue করতে previous page থেকে
next_cursorpass করুন। limit- ঐচ্ছিক। Page size, default
50, সর্বোচ্চ200।
Response
{
"items": [ /* OrderResponse … */ ],
"next_cursor": "37"
}
আর কোনো page না থাকলে next_cursor হলো null।
Order renew করুন¶
POST /orders/{id}/renew
Existing agent-কে আরেক 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 (আপনার একটি existing agent, যার সঙ্গে subscription bind হবে), 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 এবং সর্বশেষ 200 ledger events।
Query parameters
include_paid- ঐচ্ছিক, default
true। শুধু এখনও paid out হয়নি এমন events দেখতেfalseset করুন।
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 দেখাতে সুবিধাজনক, নিজে 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, এবং MintOffice dashboard banner যে readiness / section-ভিত্তিক status দিয়ে চলে, সেটিও।
এটি এমন integration agents-এর জন্য তৈরি, যাদের dashboard scrape না করে বা operator-কে জিজ্ঞেস না করেই জানতে হয় partner কীভাবে সেট আপ করা আছে। সাধারণ 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-এর ক্ষেত্রে dashboard-এ আগে থেকেই দেখানো সেই 8-character display prefix-টিও আসে, যাতে 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, এবং বাড়তি tooling বসায় — central কখনো নিজে repo চালায় না। Custom branding চাইলেrepo_url-কে আপনার GitHub HTTPS agent customization repo-তে point করুন।api.webhook_secret_prefix- Webhook signing secret-এর প্রথম 8 character; শুধু 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-এর তালিকা।
readytrueহলে খালি থাকে। আপনার নিজের admin-এ "এখনও কী বাকি" UI চালানোর মতো যথেষ্ট stable। readiness.dns_blocker- DNS provider check fail করলে মানুষের পড়ার মতো একটি কারণ (যেমন
partner has no zone.ee username configured)। অন্যথায়null। section_status.*- Dashboard-এর section-ভিত্তিক 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-এর জন্য বিক্রয়যোগ্য package-এর তালিকা — 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 হলো কোন packages আছে আর সেগুলোর দাম কত তার source of truth, আপনার 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 হলো মাসিক amount। 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-এর সময় সেভ করা 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 যা নেয়:
""বা"@"— 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 character, শুরুতে বা শেষে - থাকবে না।
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 record আর বাকি নেই।
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 হলো GET /dns/records-এর দেওয়া vendor identifier (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-তে বদলান। |
| 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-এর সময় খালি রাখুন। দুটোই set হলে তবেই deliveries যায় — 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, awaiting payment. |
order.paid |
Stripe payment confirm করেছে। Revenue event recorded। |
order.cancelled |
Order timed out হয়েছে বা explicitly cancelled হয়েছে। |
agent.provisioning_started |
এই order-এর জন্য deploy pipeline started। |
agent.ready |
Deploy succeeded। 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 হিসেবে ব্যবহার করুন — retries একই id reuse করে, তাই ওই column-এ row-level INSERT … ON CONFLICT DO NOTHING আপনার handler নিরাপদ রাখে।
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 বহন করে, যাতে আপনি programmatically branch করতে পারেন। message field human-readable hint এবং releases-এর মধ্যে change হতে পারে। 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 |
Authorization header missing বা malformed। |
invalid_api_key |
API key unknown বা rotate out হয়ে গেছে। |
rate_limited |
Per-partner বা per-IP rate limit exceeded — Retry-After দেখুন। |
missing_idempotency_key |
Idempotency-Key ছাড়া POST request। |
idempotency_key_mismatch |
একই key different request body দিয়ে reuse করা হয়েছে। |
validation_error |
Request body schema validation failed। |
not_found |
Resource নেই বা আপনার partner-এর নয়। |
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¶
- প্রতি partner 120 requests / 60 s rolling window। Burst এবং steady-state একই bucket share করে।
- Unauthenticated এবং failed-auth requests-এর জন্য আলাদা per-IP bucket: 60 requests / 60 s — bearer brute-force-কে partner quota খেয়ে ফেলা থেকে আটকায়।
- Excess requests
Retry-Afterheader (কত seconds back off করতে হবে) সহ429 rate_limitedreturn করে।
Help দরকার?¶
এই ডকস সেই partners-দের জন্য লেখা যারা সত্যিই API ব্যবহার করেন। কিছু missing, confusing, বা stale হলে আপনার mintbot agent-কে বলুন — তারা feedback forward করবে এবং আমরা page update করব।