Brand Partner API¶
Isang maliit at predictable na REST API para gumawa ng mintbot orders, mag-poll ng status nila, at tumanggap ng lifecycle events. JSON in, JSON out. Bearer-token auth. Idempotent writes. Signed webhooks.
API access dashboard Tumalon sa webhooks
Mabilisang overview¶
| Base URL | https://mint.mintbot.ai/api/v1 |
| Auth | Authorization: Bearer mo_live_… |
| Idempotency | Idempotency-Key: <uuid> sa bawat POST maliban sa POST /dns/records |
| Content type | application/json |
| Rate limit | 120 req / 60 s bawat partner |
| Webhooks | Signed gamit ang HMAC-SHA256, nire-retry hanggang 7 beses |
Authentication¶
Bawat request ay nagpapadala ng partner API key sa Authorization header. I-generate o i-rotate ang key sa dashboard.
Authorization: Bearer mo_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
Walang grace window ang rotation
Kapag ni-rotate ang key, atomically nare-revoke ang nauna. Planuhin na palitan ang value sa iyong config bago i-click ang Rotate.
Idempotency¶
Bawat POST ay nangangailangan ng Idempotency-Key header — maliban sa POST /dns/records, na natural na idempotent sa provider layer at tumatanggap ng retries kahit wala nito. Gumagana ang anumang UUID para sa bawat distinct request.
- Kina-cache namin ang response sa loob ng 24 oras. Ang retry gamit ang parehong key ay ire-replay ang orihinal na response na may
Idempotent-Replay: true. - Ang paggamit ulit ng parehong key na may ibang body ay magbabalik ng
409 idempotency_key_mismatch.
Orders¶
Gumawa ng order¶
POST /orders
Gumagawa ng order at nagbabalik ng Stripe Checkout URL. Kapag na-confirm ang payment, ipo-provision ng mintbot ang agent at awtomatikong ire-record ang partner-cut revenue event.
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- Isa sa
mo_test(Test 24h),mo_month(Standard),mo_month_plus(Performance). duration_months- Bilang ng calendar months ng buhay ng server. Dapat isa sa
1,3, o12. Angmo_testay laging iisang 24-oras na run, anuman ang ipadala mo. credit_usd- Opsyonal, default
0. Isa sa0,5,10,20,50. Pre-funded na chat credit (USD) na kasama sa order. Idinadagdag ito sa order total at napupunta sa mintbot — hindi ito bahagi ng margin mo. language- Opsyonal. Nakakaapekto sa Stripe Checkout locale at welcome-email language.
external_id- Opsyonal. Ibabalik sa bawat webhook at order response — gamitin ito para i-thread ang mintbot orders sa rows sa sarili mong system.
success_url·cancel_url- Stripe Checkout return URLs. Ang
{ORDER_ID}ay pinapalitan server-side. webhook_url- Opsyonal. Per-order override ng partner-level webhook URL.
product_name- Opsyonal. Ang line-item label na nakikita ng customer sa Stripe Checkout (hanggang 250 character). Default ay generated na label na
TIER · N months. price_usd- Opsyonal. Price override sa USD para sa buong order. Hindi ito puwedeng bumaba sa wholesale floor mo para sa tier at duration na iyon — kung hindi,
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
}
Kung nasa custom pricing mode ka at hindi mo pa napepresyuhan ang tier na ito, may kasama ring "pricing_fallback": true ang response.
Halimbawa
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"]
Kunin ang order¶
GET /orders/{id}
Kinukuha ang isang order gamit ang mintbot id nito. Nagbabalik ng parehong shape gaya ng POST /orders.
curl https://mint.mintbot.ai/api/v1/orders/42 \
-H "Authorization: Bearer $MINTBOT_API_KEY"
I-list ang orders¶
GET /orders
Cursor-paginated list, pinakabago muna.
Query parameters
status- Opsyonal. I-filter ayon sa
pending,completed, oexpired. Nananatilingcompletedang bayad na order sa buong deploy nito — sundan ang progreso ng deploy sa pamamagitan ngagent.*webhooks. cursor- Opsyonal. Ipasa ang
next_cursormula sa nakaraang page para magpatuloy. limit- Opsyonal. Laki ng page, default
50, max200.
Response
{
"items": [ /* OrderResponse … */ ],
"next_cursor": "37"
}
Ang next_cursor ay null kapag wala nang mga page.
I-renew ang order¶
POST /orders/{id}/renew
Pinapahaba ang existing agent para sa panibagong duration_months. Infra-only — walang bagong chat credit na naka-bundle. Nagbabalik ng bagong order id at fresh na Stripe Checkout URL.
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¶
Gumawa ng subscription¶
POST /subscriptions
Nagbubukas ng Stripe Checkout sa subscription mode: buwan-buwang nagbabayad ang customer para sa monthly package at awtomatikong nire-renew ang subscription. Tulad ng POST /orders, kailangan nito ng Idempotency-Key at partner account na live. Ang buwanang presyo ay mula sa pricing mo para sa tier na iyon.
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"
}
Opsyonal ang customer_email, agent_id (isang existing agent mo kung saan itatali ang subscription), external_id, at product_name.
Response — 201 Created ay may 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, at created_at.
Kunin ang subscription¶
GET /subscriptions/{id}
Nagbabalik ng parehong shape gaya ng POST /subscriptions.
Revenue¶
Basahin ang revenue¶
GET /revenue
Totals plus ang pinakabagong 200 ledger events.
Query parameters
include_paid- Opsyonal, default
true. I-set safalsepara makita lang ang events na hindi pa paid out.
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¶
Kunin ang profile¶
GET /partner
Ibabalik ang iyong partner profile kasama ang unpaid balance — useful para ipakita ang earnings sa loob ng sarili mong admin UI nang hindi mo kailangang i-store ang mga ito mismo.
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¶
Kunin ang settings¶
GET /settings
Nagbabalik ng buo mong non-secret partner configuration sa isang payload — napiling modes, apex domain, DNS provider, bot provider, Agent customization repo, subdomain pattern (may rendered preview label), per-tier pricing, API key metadata, webhook metadata, at ang parehong readiness / per-section status na nagpapatakbo sa MintOffice dashboard banner.
Dinisenyo para sa integration agents na kailangang malaman kung paano naka-set up ang partner nang hindi nag-scrape ng dashboard o nagtatanong sa operator. Ang typical caller ay coding agent na nag-i-install ng white-label setup mo sa fresh server — mababasa nito ang endpoint na ito, mag-branch sa readiness.status, at masasabi sa operator kung aling sections pa ang nangangailangan ng attention.
Walang secret na ibinabalik kailanman
Anumang ibinigay ng partner bilang credential — Telegram bot token, Zone.ee API key, webhook signing secret, mismong plaintext ng API key — lumalabas lamang bilang *_set boolean. Dagdag pa rito, ine-expose ng webhook secret ang parehong 8-char display prefix na ipinapakita na sa dashboard, kaya mako-confirm ng caller aling secret ang naka-configure nang hindi kailanman nakikita ang value.
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
}
}
Sanggunian ng mga field¶
status- Lifecycle ng partner. Isa sa
onboarding,live,suspended. Nire-reject ng Brand Partner API ang writes maliban kungliveang status; ang mintbot team lang ang naglilipat sa iyo salive. domain.modeown(dala mo ang sarili mong apex domain) omintbot(nagpo-provide ang mintbot ng*.mintbot.aisubdomain). Kapagmintbot, sadyang walang laman ang DNS-provider fields kahit ano pa ang dating inilagay ng partner.domain.subdomain_label_preview- Ang rendered label na matatanggap ng unang customer agent (
agent1,1,001, …) base sa kasalukuyangsubdomain_prefix,subdomain_numbering, atsubdomain_pad_width. Gamitin ito para sa preview na "ito ang makikita ng customers mo" nang hindi kailangang i-reimplement ang numbering rules. bot.modeown(Telegram bot na provided ng partner),mintbot(gamit ang bot ng mintbot habang testing), onone(panel lang, walang Telegram).template.modedefault(walang Agent customization repo — tumatakbo ang agent sa plain unbranded na base) oown(ang Agent customization repo ng partner sarepo_url). Kapagown, kino-clone ng sariling VPS ng agent ang repo na iyon pagkatapos ng standard deploy at pinapatakbo anginstall.sh/update.shnito para i-apply ang persona, panel theme, at anumang extra tooling sa ibabaw ng base — hindi kailanman pinapatakbo ng central ang repo mismo. Ituro angrepo_urlsa GitHub HTTPS Agent customization repo mo kapag gusto mo ng custom branding.api.webhook_secret_prefix- Unang 8 character ng webhook signing secret, naroroon lang kapag may naka-configure na secret. Hinahayaan nitong matukoy ng caller kung aling secret ang ginagamit nang hindi nakikita ang buong value. Ipinapakita rin ng dashboard ang parehong prefix.
pricing.allowed_credit_options- Ang mga
credit_usdbundle na ino-offer mo sa customers mo (pinipili sa Settings → Pricing). readiness.missing_fields- Listahan ng dashboard field names na kailangan pa bago mag-Go Live. Walang laman kapag
readyaytrue. Sapat ang stability nito para mag-drive ng "ano pa ang kulang" UI sa sarili mong admin. readiness.dns_blocker- Isang human-readable na dahilan kapag pumalya ang DNS provider check (hal.
partner has no zone.ee username configured). Kung hindi,null. section_status.*- Katulad ng per-section status pills sa dashboard. Ang
statusayok,warn, odefault(default ng mintbot ang ginagamit); pinapangalanan ngissueang field na humaharang sa section na iyon (hal.client_apex_domain,telegram_bot_token,dns_blocker), onull.
Halimbawa
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¶
Ang listahan ng mga package na puwedeng ibenta sa storefront mo — ang mga Brand Partner package (mo_test / mo_month / mo_month_plus) kasama ang iyong resolved pricing, sa pricing currency mo. Dito mo i-render ang plan cards mo sa halip na i-hard-code ang tier slugs at mga presyo: kusang nawawala ang mga retired package, at ang pagbabago ng presyo o currency na ginawa mo sa dashboard ay umaabot sa storefront mo sa loob ng cache window nito — walang kailangang redeploy.
Ito ang endpoint na pumipigil sa storefront na mag-advertise ng mga lumang package. Tinatawag ito ng reference portal sa bawat render ng landing / /buy / /extend (naka-cache, may sticky fallback).
Sa iyo pa rin ang display copy
Ang display_name / description / featured ay ang canonical na package metadata ng mintbot. Kung gusto mong palitan ang pangalan ng package o isulat muli ang blurb nito para sa brand mo, i-override ito sa sarili mong storefront (may PLAN_OVERRIDES map ang reference portal para mismo rito) — ang catalog ang source of truth kung aling mga package ang umiiral at magkano ang mga ito, hindi ang marketing copy mo.
Kunin ang catalog¶
GET /api/v1/catalog
Walang parameters. Bearer auth lang — walang activation gate, kaya puwedeng i-render ng storefront ang catalog kahit hindi pa naka-live ang partner account mo.
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 | Kahulugan |
|---|---|
currency |
Ang pricing currency mo. Lahat ng price_cents sa ibaba ay nasa currency na ito. |
packages[] |
Isang entry bawat public package, sunod sa offer/display order. |
tier |
Ang slug na ipinapasa mo sa POST /orders / POST /subscriptions. |
display_name / description / featured |
Canonical na display copy (i-override sa storefront mo kung gusto mo). |
default_credit_usd |
Ang hint ng LLM credit na kasama sa package (informational lang). |
durations[] |
Ang mga setup duration na puwedeng bilhin para sa package na ito. Ang months ay ang value na ipinapadala mo bilang duration_months; ang price_cents ang sisingilin ng order. Nag-a-advertise ang monthly packages ng 1 / 3 / 12 buwan; isang 24-oras na duration lang ang sa mo_test. |
subscription |
Ang available ay true para sa packages na puwedeng bilhin bilang monthly auto-renew sa POST /subscriptions; ang price_cents ang buwanang halaga. Hindi kailanman puwedeng i-subscribe ang mo_test. |
credit_options[] |
Bawat credit_usd bundle na may presyo sa currency mo (usd = halaga ng credit, price_cents = idinadagdag nito sa order total). Kung aling bundles ang ino-offer mo ay nasa pricing.allowed_credit_options ng GET /settings. |
Tugma ang mga presyo sa sisingilin sa iyo
Kinukuwenta ang bawat price_cents gamit ang parehong pricing resolver na ginagamit ng order endpoint, kaya laging katumbas ng presyo sa card ang huling Stripe Checkout total para sa (tier, duration_months) na iyon.
Halimbawa¶
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¶
Madalas kailangan ng brand-partner integrations na mag-provision ng DNS records sa apex domain ng partner — itinuturo ang @, www, o per-customer subdomains sa tamang server. Nag-e-expose ang MintOffice ng vendor-blind proxy para magawa mo ito nang hindi kailanman hinahawakan ang API key ng upstream provider sa sarili mong infrastructure. Ang credentials na sinave mo noong onboarding (ngayon: zone.ee; naka-roadmap ang cloudflare at route53 sa likod ng parehong shape) ay nananatiling encrypted sa MintOffice; pumapasok ang call mo gamit ang Bearer mo_live_… at kami ang gumagawa ng upstream call para sa iyo.
Ang scope ay sariling configured apex ng partner (domain.client_apex_domain mula sa GET /settings). Hindi mo maaaring galawin ang record sa zone na hindi mo pagmamay-ari.
Kung Manual ang DNS provider mo, walang binabago ang proxy: no-op ang writes at walang laman ang lists na ibinabalik.
Sa ngayon, sinusuportahan ng proxy ang operations na ginagamit mismo ng deploy pipeline — A-records (upsert / list / delete by id). Susunod sa parehong shape ang CNAME / TXT / MX support kapag pinalawak na ng underlying provider abstraction ang mga iyon.
Hindi kailangan ang Idempotency-Key dito
Hindi tulad ng order-creation endpoints, natural na idempotent ang DNS upserts sa provider layer (ang muling pag-run gamit ang parehong (subdomain, ip) ay no-op at nililinis ang stale duplicates sa bawat call). Hindi mo kailangang ipadala ang Idempotency-Key header — palaging nagko-converge ang retries sa iisang A-record per FQDN.
Mag-upsert ng A-record¶
POST /dns/records
Idempotently ituro ang <subdomain>.<your-apex> sa isang IPv4 address. Ipasa ang "" (o "@") para sa mismong apex.
Request
{
"subdomain": "www",
"ip": "203.0.113.10"
}
Tinatanggap ng subdomain ang:
""o"@"— ang mismong apex (example.com).- Isang DNS label —
"www","api"— para sa<label>.<apex>. - Dotted sub-labels —
"api.eu"— para sa nested subdomains (api.eu.example.com).
Ang ip ay dapat dotted IPv4 address. Bawat label ay [a-z0-9_-], 1–63 chars, walang leading/trailing -.
Response — 200
{
"apex": "example.com",
"provider": "zone_ee",
"fqdn": "www.example.com",
"records": [
{ "id": "9182734", "name": "www.example.com", "destination": "203.0.113.10" }
]
}
Palaging ipinapakita ng post-write records list ang kasalukuyang view ng upstream provider sa FQDN pagkatapos ng duplicate sweep — kaya ang successful response na may eksaktong isang record ang happy-path signal na wala nang stale na natitira.
I-list ang records¶
GET /dns/records
Ibinabalik ang bawat A-record sa configured apex. Idagdag ang ?name=<label-or-fqdn> para mag-filter sa isang FQDN — maaaring bare label ("www"), dotted subdomain ("api.eu"), o full FQDN ("www.example.com") ang value.
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" }
]
}
Mag-delete ng record¶
DELETE /dns/records/{record_id}
Ang record_id ay vendor identifier na ibinalik ng GET /dns/records (nag-i-issue ang zone.ee ng integer ids, na sini-stringify sa API boundary). Ang pag-delete ng record na wala na ay itinuturing na success — idempotent ang endpoint sa client side.
Response — 200
{ "deleted": true, "id": "9182734" }
Mga error¶
| HTTP | error.code |
Sanhi |
|---|---|---|
| 401 | unauthenticated / invalid_api_key |
Kulang / mali ang Bearer token. |
| 422 | validation_error |
Mali ang subdomain o non-IPv4 destination. |
| 422 | dns_not_configured |
Walang client_apex_domain ang partner o nawawala ang credentials ng configured provider. Ayusin sa Settings → Domain. |
| 422 | dns_provider_unsupported |
Hindi pa naka-wire ang DNS provider ng partner sa MintOffice proxy (hal. cloudflare placeholder). Lumipat muna sa zone_ee. |
| 502 | dns_provider_error |
Nagbalik ang upstream provider ng 4xx/5xx, o pumalya ang transport. Dala ng error message ang upstream status at maikling secret-redacted excerpt ng upstream body. Safe na mag-retry. |
Halimbawa¶
# 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¶
Kapag may nangyari sa isang order, nag-POST kami ng signed JSON event sa configured webhook URL mo.
Opsyonal ang webhooks
Ang Webhook URL at Webhook signing secret sa Settings → API access ay ganap na opsyonal — iwanang blank ang mga ito habang onboarding kung wala ka pang receiver. Saka lang may ipapadalang deliveries kapag naka-set na ang pareho — kung may URL pero walang signing secret, walang event na ipapadala. Gumagana nang maayos ang dashboard at ang natitirang API kahit walang webhooks; puwede mong i-poll ang GET /api/v1/orders/{id} para i-track ang lifecycle sa halip. Idagdag ang URL (at i-generate ang secret) kapag ready ka na, walang kailangang re-activation.
Mga uri ng event¶
| Event | Kailan ito nagti-trigger |
|---|---|
order.created |
Nagawa ang Stripe Checkout session, naghihintay ng payment. |
order.paid |
Kinumpirma ng Stripe ang payment. Na-record ang revenue event. |
order.cancelled |
Nag-time out ang order o tahasang kinansela. |
agent.provisioning_started |
Nagsimula ang deploy pipeline para sa order na ito. |
agent.ready |
Successful ang deploy. May panel_url at expires_at ang payload. |
agent.failed |
Nag-error ang deploy pipeline. Pinapangalanan ng error_step ang step na pumalya; maikling buod ang error_message. |
agent.expired |
Lumipas na ang Agent TTL. Maaaring mag-renew ang partner gamit ang POST /orders/{id}/renew. |
agent.resumed |
Na-renew ang dating expired na agent sa loob ng grace window nito at live na ulit. Recovery counterpart ng agent.expired. |
subscription.created · subscription.activated · subscription.updated · subscription.cancelled |
Lifecycle ng subscription mula sa POST /subscriptions. |
partner.activated · partner.deactivated |
In-activate ng mintbot team ang account mo o inalis ito sa live (sinasabi ng suspended sa payload kung na-suspend ito o ibinalik sa onboarding). |
Delivery at retries¶
- Hanggang 7 attempts sa exponential schedule:
0s, 30s, 2m, 10m, 1h, 6h, 24h. - Pagkatapos ng huling attempt, mamamarkahan ang delivery bilang
exhaustedat hihinto sa retrying. - Mag-respond ng
2xxstatus sa loob ng 10 seconds para i-acknowledge.
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
}
Pag-verify ng signature¶
Ang signature ay HMAC-SHA256(secret, "{timestamp}.{raw_body}"). Palaging i-verify ang raw request body — masisira ang comparison kapag ni-re-serialise mo ang parsed JSON.
import hmac, hashlib, time
def verify(secret: str, body: bytes, header: str, tolerance: int = 300) -> bool:
try:
ts_part, v1_part = header.split(",", 1)
ts = int(ts_part.split("=", 1)[1])
sig = v1_part.split("=", 1)[1]
except Exception:
return False
if abs(int(time.time()) - ts) > tolerance:
return False
expected = hmac.new(
secret.encode(),
f"{ts}.".encode() + body,
hashlib.sha256,
).hexdigest()
return hmac.compare_digest(expected, sig)
const crypto = require("crypto");
function verify(secret, rawBody, header, toleranceSec = 300) {
const [tsPart, v1Part] = header.split(",");
const ts = Number(tsPart.split("=")[1]);
const sig = v1Part.split("=")[1];
if (Math.abs(Date.now() / 1000 - ts) > toleranceSec) return false;
const expected = crypto
.createHmac("sha256", secret)
.update(`${ts}.`)
.update(rawBody)
.digest("hex");
return crypto.timingSafeEqual(
Buffer.from(expected, "hex"),
Buffer.from(sig, "hex"),
);
}
Idempotent receivers
Gamitin ang X-Mintbot-Event-Id bilang dedup key mo — ginagamit muli ng retries ang parehong id, kaya pinapanatiling safe ng row-level INSERT … ON CONFLICT DO NOTHING sa column na iyon ang handler mo.
Reference portal¶
May runnable end-to-end reference sa mintbot-ai/partner-portal-example — FastAPI + SQLite, Dockerised, may kasamang example brand na tinatawag na ExampleAI na ifo-fork at papalitan mo. Ang architectural picture — kung paano magkakasya ang storefront, itong API, deploy worker, at agent template — ay nasa MintOffice — Technical. Ini-implement ng portal ang:
- landing / plan-picker / thank-you / cancel pages,
POST /buy→POST /api/v1/orders→ Stripe redirect,POST /webhooks/mintofficekasama ang HMAC verifier na nire-reference sa itaas,- isang HTTP-Basic-auth
/adminevent browser para makita mo ang deliveries.
I-fork ito, i-set ang PARTNER_BRAND, MINTOFFICE_API_KEY, at MINTOFFICE_WEBHOOK_SECRET sa .env, patakbuhin ang docker compose up, at ituro ang webhook URL ng partner row mo sa https://<your-host>/webhooks/mintoffice. Tingnan ang MintOffice → Reference portal para sa buong walkthrough.
Mga error¶
Bawat error response ay may stable na code na puwede mong i-branch programmatically. Ang message field ay human-readable hint at maaaring magbago sa pagitan ng releases. Ine-echo ng request_id ang X-Request-Id response header — isama ito sa support tickets.
{
"error": {
"code": "invalid_api_key",
"message": "API key is unknown or revoked.",
"request_id": "f0c2d6c4-…"
}
}
| Code | Kahulugan |
|---|---|
unauthenticated |
Kulang o malformed ang Authorization header. |
invalid_api_key |
Hindi kilala ang API key o na-rotate out na. |
rate_limited |
Lumampas sa per-partner o per-IP rate limit — tingnan ang Retry-After. |
missing_idempotency_key |
POST request na walang Idempotency-Key. |
idempotency_key_mismatch |
Ginamit muli ang parehong key na may ibang request body. |
validation_error |
Pumalya ang request body sa schema validation. |
not_found |
Wala ang resource o hindi ito kabilang sa partner mo. |
partner_not_live |
HTTP 403 — isang write (POST /orders, /orders/{id}/renew, /subscriptions) habang hindi live ang account mo. May status ang body, at para sa onboarding accounts, missing_fields + dns_blocker. |
conflict |
HTTP 409 — pinoproseso pa ang request na may parehong Idempotency-Key. Subukang muli pagkatapos ng Retry-After. |
unpriced_tier |
HTTP 409 — walang presyong masisingil para sa tier. |
internal_error |
Hindi inaasahang error sa server side. Isama ang request_id kapag kumontak sa support. |
payment_gateway_error |
Ni-reject ng Stripe ang Checkout session creation. |
dns_not_configured |
POST /dns/records laban sa partner na walang apex / walang DNS credentials. Ayusin sa Settings → Domain. |
dns_provider_unsupported |
Hindi pa naka-wire ang dns_provider value ng partner sa MintOffice proxy. |
dns_provider_error |
Nagbalik ng error ang upstream DNS provider — may redacted excerpt ang message. |
quota_exceeded |
HTTP 429, sa DEV environment (mint.mintbot.dev) lang — walang per-partner cap sa LIVE. Naubos na ng partner ang lahat ng deploy slot (10 bilang default, 20 pagkatapos ng Brand Partner setup fee). Kontakin ang mintbot support para itaas ang cap. Ibinabalik ng POST /orders at POST /orders/{id}/renew. May deploys_used + deploy_quota ang body. |
Rate limits¶
- 120 requests / 60 s rolling window, per partner. Magkapareho ang bucket ng burst at steady-state.
- 60 requests / 60 s hiwalay na per-IP bucket para sa unauthenticated at failed-auth requests — pinipigilan nitong ubusin ng bearer brute-force ang partner quota.
- Nagbabalik ang excess requests ng
429 rate_limitedkasama angRetry-Afterheader (seconds bago mag-back off).
Kailangan ng tulong?¶
Isinulat ang docs na ito para sa partners na aktwal na gumagamit ng API. Kung may kulang, nakakalito, o stale, banggitin ito sa mintbot agent mo — ipapasa nila ang feedback at ia-update namin ang page.