Brand Partner API¶
Ett litet, förutsägbart REST API för att skapa mintbot-ordrar, kontrollera deras status och ta emot livscykelhändelser. JSON in, JSON ut. Bearer-token-auth. Idempotenta skrivningar. Signerade webhooks.
Dashboard för API-åtkomst Hoppa till webhooks
Snabb översikt¶
| Bas-URL | https://mint.mintbot.ai/api/v1 |
| Auth | Authorization: Bearer mo_live_… |
| Idempotens | Idempotency-Key: <uuid> på varje POST utom POST /dns/records |
| Innehållstyp | application/json |
| Hastighetsgräns | 120 req / 60 s per partner |
| Webhooks | Signeras med HMAC-SHA256, försöks igen upp till 7 gånger |
Autentisering¶
Varje begäran skickar en partner-API-nyckel i headern Authorization. Generera eller rotera nyckeln i dashboarden.
Authorization: Bearer mo_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json
Rotation har ingen respitperiod
När nyckeln roteras spärras den föregående atomärt. Planera att byta värdet i din config innan du klickar på Rotate.
Idempotens¶
Varje POST kräver headern Idempotency-Key — utom POST /dns/records, som är naturligt idempotent i leverantörslagret och accepterar nya försök utan den. Vilken UUID som helst per unik begäran fungerar.
- Vi cachar svaret i 24 timmar. Ett nytt försök med samma nyckel spelar upp det ursprungliga svaret med
Idempotent-Replay: true. - Om samma nyckel återanvänds med en annan body returneras
409 idempotency_key_mismatch.
Ordrar¶
Skapa order¶
POST /orders
Skapar en order och returnerar en Stripe Checkout-URL. När betalningen bekräftas provisionerar mintbot agenten och intäktshändelsen för partnerandelen registreras automatiskt.
Begäran
{
"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- En av
mo_test(Test 24h),mo_month(Standard),mo_month_plus(Performance). duration_months- Serverns livslängd i kalendermånader. Måste vara
1,3eller12.mo_testär alltid en enda körning på 24 timmar, oavsett vad du skickar. credit_usd- Valfri, standard
0. En av0,5,10,20,50. Förbetald chattkredit (USD) som ingår i ordern. Den läggs till ordersumman och går till mintbot — den ingår inte i din marginal. language- Valfritt. Påverkar språket i Stripe Checkout och välkomstmejlet.
external_id- Valfritt. Ekas tillbaka i varje webhook och ordersvar — använd det för att koppla mintbot-ordrar till rader i ditt eget system.
success_url·cancel_url- Retur-URL:er för Stripe Checkout.
{ORDER_ID}ersätts på serversidan. webhook_url- Valfritt. Åsidosättning per order av webhook-URL:en på partnernivå.
product_name- Valfri. Radetiketten som kunden ser i Stripe Checkout (högst 250 tecken). Standard är en genererad etikett av typen
TIER · N months. price_usd- Valfri. Åsidosatt pris i USD för hela ordern. Det kan inte understiga ditt grossistgolv för den nivån och perioden — annars
400 validation_error.
Svar — 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
}
Om du använder anpassad prissättning och inte har satt ett pris för den här nivån innehåller svaret också "pricing_fallback": true.
Exempel
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"]
Hämta order¶
GET /orders/{id}
Hämtar en enskild order via dess mintbot-id. Returnerar samma form som POST /orders.
curl https://mint.mintbot.ai/api/v1/orders/42 \
-H "Authorization: Bearer $MINTBOT_API_KEY"
Lista ordrar¶
GET /orders
Markörpaginering, nyast först.
Query-parametrar
status- Valfri. Filtrera på
pending,completedellerexpired. En betald order förblircompletedunder hela sin deploy — följ deployens förlopp viaagent.*-webhookarna. cursor- Valfritt. Skicka
next_cursorfrån föregående sida för att fortsätta. limit- Valfri. Sidstorlek, standard
50, högst200.
Svar
{
"items": [ /* OrderResponse … */ ],
"next_cursor": "37"
}
next_cursor är null när det inte finns fler sidor.
Förnya order¶
POST /orders/{id}/renew
Förlänger en befintlig agent med ytterligare duration_months. Endast infra — ingen ny chattkredit ingår. Returnerar ett nytt order-id och en ny Stripe Checkout-URL.
Begäran
{
"duration_months": 1,
"external_id": "your-side-id",
"success_url": "https://your.app/thanks?id={ORDER_ID}",
"cancel_url": "https://your.app/account"
}
Abonnemang¶
Skapa abonnemang¶
POST /subscriptions
Öppnar en Stripe Checkout i abonnemangsläge: kunden betalar varje månad för ett månadspaket och abonnemanget förnyas automatiskt. Precis som POST /orders kräver det en Idempotency-Key och ett partnerkonto med status live. Månadspriset kommer från din prissättning för den nivån.
Begäran
{
"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 (en befintlig agent hos dig som abonnemanget ska kopplas till), external_id och product_name är valfria.
Svar — 201 Created innehåller 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 och created_at.
Hämta abonnemang¶
GET /subscriptions/{id}
Returnerar samma struktur som POST /subscriptions.
Intäkter¶
Läs intäkter¶
GET /revenue
Totalsummor plus de senaste 200 ledger-händelserna.
Query-parametrar
include_paid- Valfritt, standard
true. Sätt tillfalseför att bara se händelser som ännu inte har betalats ut.
Svar
{
"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
}
]
}
Partnerprofil¶
Hämta profil¶
GET /partner
Ekar din partnerprofil plus obetalt saldo — praktiskt för att visa intäkter i ditt eget admin-UI utan att lagra dem själv.
Svar
{
"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"
}
Inställningar¶
Hämta inställningar¶
GET /settings
Returnerar hela din icke-hemliga partnerkonfiguration i en enda payload — valda lägen, apex-domän, DNS-leverantör, botleverantör, anpassningsrepo, subdomänmönster (med en renderad förhandsetikett), prissättning per nivå, metadata för API-nyckeln och webhooken samt samma beredskaps- och sektionsstatus som styr bannern i MintOffice-dashboarden.
Byggd för integrationsagenter som behöver ta reda på hur partnern är uppsatt utan att skrapa dashboarden eller fråga operatören. En typisk anropare är en kodagent som installerar din white label-uppsättning på en ny server — den kan läsa den här endpointen, förgrena på readiness.status och tala om för operatören exakt vilka sektioner som fortfarande behöver åtgärdas.
Inga hemligheter returneras någonsin
Allt som partnern har angett som inloggningsuppgift — Telegram-bottoken, Zone.ee-API-nyckeln, webhookens signeringshemlighet, själva API-nyckeln i klartext — visas bara som en *_set-boolean. För webhookhemligheten visas dessutom samma 8 tecken långa visningsprefix som redan syns i dashboarden, så att anroparen kan bekräfta vilken hemlighet som är konfigurerad utan att någonsin se värdet.
Svar
{
"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
}
}
Fältreferens¶
status- Partnerns livscykel. En av
onboarding,live,suspended. Brand Partner API avvisar skrivningar om statusen inte ärlive; bara mintbot-teamet kan flytta dig tilllive. domain.modeown(du använder din egen apex-domän) ellermintbot(mintbot tillhandahåller en*.mintbot.ai-subdomän). Vidmintbotär DNS-leverantörsfälten avsiktligt tomma, oavsett vad partnern tidigare har angett.domain.subdomain_label_preview- Den renderade etikett som den första kundagenten skulle få (
agent1,1,001, …) med nuvarandesubdomain_prefix,subdomain_numberingochsubdomain_pad_width. Använd den för en förhandsvisning av typen ”så här ser dina kunder det” utan att implementera numreringsreglerna själv. bot.modeown(partnerns egen Telegram-bot),mintbot(mintbots bot används under testning) ellernone(bara panelen, ingen Telegram).template.modedefault(inget anpassningsrepo för agenten — agenten kör på den rena basen utan varumärke) ellerown(partnerns anpassningsrepo för agenten pårepo_url). Vidownklonar agentens egen VPS repot efter standarddeployen och kör dessinstall.sh/update.shför att lägga din persona, ditt paneltema och eventuella extra verktyg ovanpå basen — central kör aldrig repot själv. Pekarepo_urlpå ditt anpassningsrepo på GitHub (HTTPS) när du vill ha eget varumärke.api.webhook_secret_prefix- De första 8 tecknen i webhookens signeringshemlighet; finns bara när en hemlighet är konfigurerad. Låter anroparen skilja på vilken hemlighet som används utan att se hela värdet. Dashboarden visar samma prefix.
pricing.allowed_credit_options- De
credit_usd-alternativ du erbjuder dina kunder (väljs i Settings → Pricing). readiness.missing_fields- Lista över dashboardfält som fortfarande krävs före Go Live. Tom när
readyärtrue. Tillräckligt stabil för att driva ett ”vad saknas fortfarande”-gränssnitt i din egen admin. readiness.dns_blocker- En läsbar orsak när kontrollen av DNS-leverantören misslyckas (t.ex.
partner has no zone.ee username configured). Annarsnull. section_status.*- Speglar dashboardens statusmärken per sektion.
statusärok,warnellerdefault(ett standardvärde från mintbot används);issueanger fältet som blockerar sektionen (t.ex.client_apex_domain,telegram_bot_token,dns_blocker), ellernull.
Exempel
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"])
Katalog¶
Listan över säljbara paket för din butik — Brand Partner-paketen (mo_test / mo_month / mo_month_plus) kombinerade med din fastställda prissättning, i din prisvaluta. Rendera dina paketkort utifrån den i stället för att hårdkoda nivåslugs och priser: paket som tas bort försvinner av sig själva, och en pris- eller valutaändring du gör i dashboarden når din butik inom cachefönstret — ingen ny deploy behövs.
Det är den här endpointen som hindrar en butik från att marknadsföra gårdagens paket. Referensportalen anropar den vid varje rendering av startsidan, /buy och /extend (cachad, med en beständig reserv).
Visningstexterna bestämmer du
display_name / description / featured är mintbots kanoniska paketmetadata. Vill du byta namn på ett paket eller skriva om beskrivningen för ditt varumärke gör du det i din egen butik (referensportalen har en PLAN_OVERRIDES-map just för detta) — katalogen är sanningskällan för vilka paket som finns och vad de kostar, inte för dina marknadsföringstexter.
Hämta katalog¶
GET /api/v1/catalog
Inga parametrar. Bara Bearer-autentisering — det finns ingen aktiveringsspärr, så en butik kan rendera katalogen innan ditt partnerkonto har gått live.
Svar
{
"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 }
]
}
| Fält | Betydelse |
|---|---|
currency |
Din prisvaluta. Alla price_cents nedan anges i den. |
packages[] |
En post per publikt paket, i erbjudande-/visningsordning. |
tier |
Slugen du skickar till POST /orders / POST /subscriptions. |
display_name / description / featured |
Kanoniska visningstexter (åsidosätt dem i din butik om du vill). |
default_credit_usd |
Paketets riktvärde för inkluderad LLM-kredit (informativt). |
durations[] |
De uppsättningsperioder paketet kan köpas för. months är värdet du skickar som duration_months; price_cents är vad ordern debiterar. Månadspaket erbjuder 1 / 3 / 12 månader; mo_test erbjuder en enda period på 24 timmar. |
subscription |
available är true för paket som kan köpas som månatlig automatisk förnyelse via POST /subscriptions; price_cents är månadsbeloppet. mo_test kan aldrig köpas som abonnemang. |
credit_options[] |
Varje credit_usd-alternativ prissatt i din valuta (usd = kreditbelopp, price_cents = vad det lägger till ordersumman). Vilka alternativ du erbjuder anges av pricing.allowed_credit_options i GET /settings. |
Priserna motsvarar det som faktiskt debiteras
Varje price_cents beräknas med samma prislogik som order-endpointen använder, så ett kortpris är alltid lika med den slutliga Stripe Checkout-summan för den (tier, duration_months).
Exempel¶
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-poster¶
Brand Partner-integrationer behöver ofta skapa DNS-poster på partnerns apex-domän — för att peka @, www eller subdomäner per kund mot rätt server. MintOffice erbjuder en leverantörsoberoende proxy så att du kan göra det utan att någonsin hantera den underliggande leverantörens API-nyckel i din egen infrastruktur. Inloggningsuppgifterna du sparade vid onboardingen (i dag zone.ee; cloudflare och route53 finns på färdplanen med samma struktur) förblir krypterade i MintOffice; ditt anrop kommer in med Bearer mo_live_… och vi gör anropet till leverantören åt dig.
Omfånget är partnerns egen konfigurerade apex (domain.client_apex_domain från GET /settings). Du kan inte röra en post i en zon som du inte äger.
Om din DNS-leverantör är Manual ändrar proxyn ingenting: skrivningar gör ingenting och listor returneras tomma.
I dag stöder proxyn de operationer som deploy-pipelinen själv använder — A-poster (upsert / lista / radera via id). Stöd för CNAME / TXT / MX följer med samma struktur när den underliggande leverantörsabstraktionen får stöd för dem.
Idempotency-Key behövs inte här
Till skillnad från endpointerna som skapar ordrar är DNS-upserts naturligt idempotenta i leverantörslagret (att köra om med samma (subdomain, ip) ändrar ingenting, och inaktuella dubbletter rensas vid varje anrop). Du behöver inte skicka headern Idempotency-Key — nya försök landar alltid i en enda A-post per FQDN.
Skapa eller uppdatera en A-post¶
POST /dns/records
Pekar idempotent <subdomain>.<your-apex> mot en IPv4-adress. Skicka "" (eller "@") för själva apex-domänen.
Begäran
{
"subdomain": "www",
"ip": "203.0.113.10"
}
subdomain accepterar:
""eller"@"— själva apex-domänen (example.com).- En enskild DNS-etikett —
"www","api"— för<label>.<apex>. - Punktseparerade underetiketter —
"api.eu"— för nästlade subdomäner (api.eu.example.com).
ip måste vara en punktseparerad IPv4-adress. Varje etikett är [a-z0-9_-], 1–63 tecken, utan inledande eller avslutande -.
Svar — 200
{
"apex": "example.com",
"provider": "zone_ee",
"fqdn": "www.example.com",
"records": [
{ "id": "9182734", "name": "www.example.com", "destination": "203.0.113.10" }
]
}
Listan records efter skrivningen visar alltid leverantörens aktuella bild av FQDN:en efter dubblettrensningen — så ett lyckat svar med exakt en post är signalen att inget inaktuellt finns kvar.
Lista poster¶
GET /dns/records
Returnerar alla A-poster på den konfigurerade apex-domänen. Lägg till ?name=<label-or-fqdn> för att filtrera på en FQDN — värdet kan vara en enskild etikett ("www"), en punktseparerad subdomän ("api.eu") eller hela FQDN:en ("www.example.com").
Svar — 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" }
]
}
Radera en post¶
DELETE /dns/records/{record_id}
record_id är leverantörens identifierare som GET /dns/records returnerar (zone.ee använder heltals-id:n som görs om till strängar vid API-gränsen). Att radera en post som inte längre finns räknas som lyckat — endpointen är idempotent från klientens sida.
Svar — 200
{ "deleted": true, "id": "9182734" }
Fel¶
| HTTP | error.code |
Orsak |
|---|---|---|
| 401 | unauthenticated / invalid_api_key |
Bearer-token saknas eller är ogiltig. |
| 422 | validation_error |
Ogiltig subdomän eller destinationen är inte IPv4. |
| 422 | dns_not_configured |
Partnern saknar client_apex_domain eller så saknas inloggningsuppgifterna för den konfigurerade leverantören. Åtgärda i Settings → Domain. |
| 422 | dns_provider_unsupported |
Partnerns DNS-leverantör är ännu inte kopplad till MintOffice-proxyn (t.ex. platshållaren för cloudflare). Byt till zone_ee tills vidare. |
| 502 | dns_provider_error |
Leverantören returnerade 4xx/5xx eller så misslyckades anslutningen. Felets message innehåller leverantörens status och ett kort utdrag ur svaret, rensat från hemligheter. Säkert att försöka igen. |
Exempel¶
# 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¶
När något händer med en order gör vi POST av en signerad JSON-händelse till din konfigurerade webhook-URL.
Webhooks är valfria
Webhook URL och Webhook signing secret under Settings → API access är helt valfria — lämna dem tomma under onboardingen om du inte har någon mottagare än. Leveranser skickas först när båda är angivna — med en URL men utan signeringshemlighet skickas inga händelser. Dashboarden och resten av API:et fungerar utmärkt utan webhooks; du kan i stället polla GET /api/v1/orders/{id} för att följa livscykeln. Lägg till URL:en (och generera hemligheten) när du är redo — ingen ny aktivering behövs.
Händelsetyper¶
| Händelse | När den utlöses |
|---|---|
order.created |
Stripe Checkout-session skapad, inväntar betalning. |
order.paid |
Stripe bekräftade betalningen. Intäktshändelse registrerad. |
order.cancelled |
Ordern gick ut eller avbröts uttryckligen. |
agent.provisioning_started |
Deploy-pipelinen startade för den här ordern. |
agent.ready |
Deploy lyckades. Payload innehåller panel_url och expires_at. |
agent.failed |
Deploy-pipelinen misslyckades. error_step anger steget som gick sönder; error_message är en kort sammanfattning. |
agent.expired |
Agentens TTL har löpt ut. Partnern kan förnya via POST /orders/{id}/renew. |
agent.resumed |
En tidigare utgången agent förnyades inom sin frist och är live igen. Återhämtningsmotsvarigheten till agent.expired. |
subscription.created · subscription.activated · subscription.updated · subscription.cancelled |
Livscykeln för ett abonnemang som skapats med POST /subscriptions. |
partner.activated · partner.deactivated |
mintbot-teamet aktiverade ditt konto eller tog bort det från live (suspended i payloaden visar om det stängdes av eller flyttades tillbaka till onboarding). |
Leverans och nya försök¶
- Upp till 7 försök enligt ett exponentiellt schema:
0s, 30s, 2m, 10m, 1h, 6h, 24h. - Efter sista försöket markeras leveransen som
exhaustedoch slutar försöka igen. - Svara med en
2xx-status inom 10 sekunder för att bekräfta.
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
Exempelpayload — 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
}
Verifiera signaturen¶
Signaturen är HMAC-SHA256(secret, "{timestamp}.{raw_body}"). Verifiera alltid den råa request-bodyn — att serialisera om din parsade JSON bryter jämförelsen.
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"),
);
}
Idempotenta mottagare
Använd X-Mintbot-Event-Id som dedupliceringsnyckel — nya försök återanvänder samma id, så en radnivå-INSERT … ON CONFLICT DO NOTHING på den kolumnen håller din handler säker.
Referensportal¶
En körbar referens från början till slut finns på mintbot-ai/partner-portal-example — FastAPI + SQLite i Docker, med ett exempelvarumärke kallat ExampleAI som du forkar och byter ut. Arkitekturbilden — hur butiken, det här API:et, deploy-workern och anpassningsrepot hänger ihop — finns på MintOffice — Teknisk. Portalen innehåller:
- start-, paketvals-, tack- och avbrytsidor,
POST /buy→POST /api/v1/orders→ omdirigering till Stripe,POST /webhooks/mintofficemed HMAC-verifieringen ovan,- en händelsebläddrare på
/adminbakom HTTP Basic-autentisering så att du snabbt kan se leveranserna.
Forka den, ange PARTNER_BRAND, MINTOFFICE_API_KEY och MINTOFFICE_WEBHOOK_SECRET i .env, kör docker compose up och peka partnerkontots webhook-URL mot https://<your-host>/webhooks/mintoffice. Hela genomgången finns i MintOffice → Referensportal.
Fel¶
Varje felsvar innehåller en stabil code som du kan förgrena på programmatiskt. Fältet message är en läsbar ledtråd och kan ändras mellan releaser. request_id ekar respons-headern X-Request-Id — ta med det i supportärenden.
{
"error": {
"code": "invalid_api_key",
"message": "API key is unknown or revoked.",
"request_id": "f0c2d6c4-…"
}
}
| Kod | Betydelse |
|---|---|
unauthenticated |
Saknad eller felformaterad Authorization-header. |
invalid_api_key |
API-nyckeln är okänd eller har roterats bort. |
rate_limited |
Rate limit per partner eller per IP har överskridits — se Retry-After. |
missing_idempotency_key |
POST-begäran utan Idempotency-Key. |
idempotency_key_mismatch |
Samma nyckel återanvändes med en annan request body. |
validation_error |
Request body klarade inte schemavalideringen. |
not_found |
Resursen finns inte eller tillhör inte din partner. |
partner_not_live |
HTTP 403 — en skrivning (POST /orders, /orders/{id}/renew, /subscriptions) medan ditt konto inte är live. Bodyn innehåller status och, för konton i onboarding, missing_fields + dns_blocker. |
conflict |
HTTP 409 — en begäran med samma Idempotency-Key behandlas fortfarande. Försök igen efter Retry-After. |
unpriced_tier |
HTTP 409 — nivån har inget pris att debitera. |
internal_error |
Oväntat fel på serversidan. Ange request_id när du kontaktar supporten. |
payment_gateway_error |
Stripe avvisade skapandet av Checkout-sessionen. |
dns_not_configured |
POST /dns/records mot en partner utan apex eller utan DNS-inloggningsuppgifter. Åtgärda i Settings → Domain. |
dns_provider_unsupported |
Partnerns dns_provider-värde är ännu inte kopplat till MintOffice-proxyn. |
dns_provider_error |
DNS-leverantören returnerade ett fel — meddelandet innehåller ett utdrag rensat från hemligheter. |
quota_exceeded |
HTTP 429, endast i DEV-miljön (mint.mintbot.dev) — LIVE har ingen gräns per partner. Partnern har förbrukat alla deploy-platser (10 som standard, 20 efter Brand Partner-startavgiften). Kontakta mintbots support för att höja gränsen. Returneras av POST /orders och POST /orders/{id}/renew. Bodyn innehåller deploys_used + deploy_quota. |
Rate limits¶
- 120 begäranden / 60 s rullande fönster, per partner. Burst och steady-state delar samma bucket.
- 60 begäranden / 60 s separat per-IP-bucket för oautentiserade och misslyckade auth-begäranden — hindrar bearer brute-force från att äta upp partnerkvoten.
- Överskjutande begäranden returnerar
429 rate_limitedmed headernRetry-After(sekunder att vänta).
Behöver du hjälp?¶
Den här dokumentationen är skriven för de partners som faktiskt använder API:t. Om något saknas, är förvirrande eller inaktuellt, nämn det för din mintbot-agent — så vidarebefordrar de feedbacken och vi uppdaterar sidan.