কনটেন্টে যান

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_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 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 months label।
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_cursor pass করুন।
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 দেখতে false set করুন।

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-এর একটি। 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, এবং বাড়তি 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_usd bundles offer করেন (Settings → Pricing-এ বেছে নেওয়া)।
readiness.missing_fields
Go Live-এর আগে এখনও দরকারি dashboard field names-এর তালিকা। ready true হলে খালি থাকে। আপনার নিজের 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 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 হিসেবে ব্যবহার করুন — 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-সুরক্ষিত একটি /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 বহন করে, যাতে আপনি 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-After header (কত seconds back off করতে হবে) সহ 429 rate_limited return করে।

Help দরকার?

এই ডকস সেই partners-দের জন্য লেখা যারা সত্যিই API ব্যবহার করেন। কিছু missing, confusing, বা stale হলে আপনার mintbot agent-কে বলুন — তারা feedback forward করবে এবং আমরা page update করব।