Gå till innehållet

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, 3 eller 12. mo_test är alltid en enda körning på 24 timmar, oavsett vad du skickar.
credit_usd
Valfri, standard 0. En av 0, 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, completed eller expired. En betald order förblir completed under hela sin deploy — följ deployens förlopp via agent.*-webhookarna.
cursor
Valfritt. Skicka next_cursor från föregående sida för att fortsätta.
limit
Valfri. Sidstorlek, standard 50, högst 200.

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 till false fö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 är live; bara mintbot-teamet kan flytta dig till live.
domain.mode
own (du använder din egen apex-domän) eller mintbot (mintbot tillhandahåller en *.mintbot.ai-subdomän). Vid mintbot ä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 nuvarande subdomain_prefix, subdomain_numbering och subdomain_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.mode
own (partnerns egen Telegram-bot), mintbot (mintbots bot används under testning) eller none (bara panelen, ingen Telegram).
template.mode
default (inget anpassningsrepo för agenten — agenten kör på den rena basen utan varumärke) eller own (partnerns anpassningsrepo för agenten på repo_url). Vid own klonar agentens egen VPS repot efter standarddeployen och kör dess install.sh / update.sh för att lägga din persona, ditt paneltema och eventuella extra verktyg ovanpå basen — central kör aldrig repot själv. Peka repo_url på 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 är true. 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). Annars null.
section_status.*
Speglar dashboardens statusmärken per sektion. status är ok, warn eller default (ett standardvärde från mintbot används); issue anger fältet som blockerar sektionen (t.ex. client_apex_domain, telegram_bot_token, dns_blocker), eller null.

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 exhausted och 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/mintoffice med HMAC-verifieringen ovan,
  • en händelsebläddrare på /admin bakom 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_limited med headern Retry-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.