Gå til indholdet

Brand Partner API

Et lille, forudsigeligt REST API til at oprette mintbot-ordrer, hente deres status og modtage lifecycle events. JSON ind, JSON ud. Bearer-token auth. Idempotente writes. Signerede webhooks.

API access dashboard Gå til webhooks


Kort overblik

Base URL https://mint.mintbot.ai/api/v1
Auth Authorization: Bearer mo_live_…
Idempotency Idempotency-Key: <uuid> på hver POST undtagen POST /dns/records
Content type application/json
Rate limit 120 req / 60 s pr. partner
Webhooks Signeret med HMAC-SHA256, forsøges igen op til 7 gange

Autentificering

Hver request sender en partner API key i Authorization headeren. Generér eller rotér key'en i dashboardet.

Authorization: Bearer mo_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Content-Type: application/json

Rotation har intet grace window

Når du roterer key'en, tilbagekaldes den tidligere atomisk. Planlæg at udskifte værdien i din config, før du klikker Rotate.

Idempotency

Hver POST kræver en Idempotency-Key header — undtagen POST /dns/records, som er naturligt idempotent i udbyderlaget og accepterer genforsøg uden den. En hvilken som helst UUID pr. unik request virker.

  • Vi cacher response i 24 timer. Et retry med samme key afspiller det oprindelige response med Idempotent-Replay: true.
  • Genbrug af samme key med en anden body returnerer 409 idempotency_key_mismatch.

Ordrer

Opret order

POST /orders

Opretter en order og returnerer en Stripe Checkout URL. Når betalingen er bekræftet, provisionerer mintbot agenten, og revenue event for partner-andelen registreres automatisk.

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
En af mo_test (Test 24h), mo_month (Standard), mo_month_plus (Performance).
duration_months
Kalendermåneder for serverens levetid. Skal være en af 1, 3 eller 12. mo_test er altid én kørsel på 24 timer, uanset hvad du sender.
credit_usd
Valgfri, standard 0. En af 0, 5, 10, 20, 50. Forudbetalt chatkredit (USD), der følger med ordren. Den lægges oven i ordretotalen og går til mintbot — den er ikke en del af din margin.
language
Valgfri. Påvirker Stripe Checkout locale og sproget i welcome-email.
external_id
Valgfri. Sendes tilbage i hver webhook og order response — brug den til at knytte mintbot-ordrer til rækker i dit eget system.
success_url · cancel_url
Stripe Checkout return URLs. {ORDER_ID} erstattes server-side.
webhook_url
Valgfri. Per-order override af webhook URL på partnerniveau.
product_name
Valgfri. Den linjeetiket, kunden ser i Stripe Checkout (maks. 250 tegn). Standard er en genereret etiket af typen TIER · N months.
price_usd
Valgfri. Tilsidesat pris i USD for hele ordren. Den kan ikke gå under dit engrosgulv for det niveau og den periode — ellers 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
}

Hvis du bruger tilpassede priser og ikke har prissat dette niveau, indeholder svaret også "pricing_fallback": true.

Eksempel

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"]

Hent order

GET /orders/{id}

Henter en enkelt order via dens mintbot id. Returnerer samme form som POST /orders.

curl https://mint.mintbot.ai/api/v1/orders/42 \
  -H "Authorization: Bearer $MINTBOT_API_KEY"

List ordrer

GET /orders

Cursor-pagineret liste, nyeste først.

Query-parametre

status
Valgfri. Filtrér efter pending, completed eller expired. En betalt order forbliver completed under hele sit deploy — følg deployets fremdrift via agent.*-webhooks.
cursor
Valgfri. Send next_cursor fra den forrige side for at fortsætte.
limit
Valgfri. Sidestørrelse, standard 50, maks. 200.

Response

{
  "items": [ /* OrderResponse … */ ],
  "next_cursor": "37"
}

next_cursor er null, når der ikke er flere sider.


Forny order

POST /orders/{id}/renew

Forlænger en eksisterende agent med yderligere duration_months. Kun infra — ingen ny chat credit følger med. Returnerer et nyt order id og en ny 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"
}

Abonnementer

Opret abonnement

POST /subscriptions

Åbner en Stripe Checkout i abonnementstilstand: kunden betaler hver måned for en månedspakke, og abonnementet fornyes automatisk. Ligesom POST /orders kræver det en Idempotency-Key og en partnerkonto med status live. Månedsprisen kommer fra dine priser for det niveau.

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 (en eksisterende agent hos dig, som abonnementet skal knyttes til), external_id og product_name er valgfri.

Response — 201 Created indeholder 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 og created_at.

Hent abonnement

GET /subscriptions/{id}

Returnerer samme form som POST /subscriptions.


Indtægter

Læs indtægter

GET /revenue

Totaler plus de seneste 200 ledger events.

Query-parametre

include_paid
Valgfri, default true. Sæt til false for kun at se events, der endnu ikke er udbetalt.

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
    }
  ]
}

Partnerprofil

Hent profil

GET /partner

Sender din partnerprofil tilbage plus den ubetalte saldo — praktisk til at vise indtjening i dit eget admin UI uden selv at gemme den.

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"
}

Indstillinger

Hent indstillinger

GET /settings

Returnerer hele din ikke-hemmelige partnerkonfiguration i én payload — valgte tilstande, apex-domæne, DNS-udbyder, botudbyder, tilpasningsrepo, subdomænemønster (med en renderet eksempeletiket), priser pr. niveau, metadata for API-nøglen og webhooken samt den samme parathed og sektionsstatus, som styrer banneret i MintOffice-dashboardet.

Lavet til integrationsagenter, der skal finde ud af, hvordan partneren er sat op, uden at skrabe dashboardet eller spørge operatøren. En typisk kalder er en kodeagent, der installerer dit white label-setup på en ny server — den kan læse dette endpoint, forgrene på readiness.status og fortælle operatøren præcis, hvilke sektioner der stadig mangler.

Hemmeligheder returneres aldrig

Alt, hvad partneren har angivet som legitimationsoplysning — Telegram-bottoken, Zone.ee-API-nøglen, webhookens signeringshemmelighed, selve API-nøglen i klartekst — vises kun som en *_set-boolean. For webhookhemmeligheden vises desuden det samme 8-tegns visningspræfiks, som allerede står i dashboardet, så kalderen kan bekræfte, hvilken hemmelighed der er konfigureret, uden nogensinde at se værdien.

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
  }
}

Feltreference

status
Partnerens livscyklus. En af onboarding, live, suspended. Brand Partner API afviser writes, medmindre status er live; kun mintbot-teamet kan flytte dig til live.
domain.mode
own (du bruger dit eget apex-domæne) eller mintbot (mintbot leverer et *.mintbot.ai-subdomæne). Ved mintbot er DNS-udbyderfelterne bevidst tomme, uanset hvad partneren tidligere har indtastet.
domain.subdomain_label_preview
Den renderede etiket, som den første kundeagent ville få (agent1, 1, 001, …) med de nuværende subdomain_prefix, subdomain_numbering og subdomain_pad_width. Brug den til en ”sådan ser dine kunder det”-forhåndsvisning uden selv at genimplementere nummereringsreglerne.
bot.mode
own (partnerens egen Telegram-bot), mintbot (mintbots bot bruges under test) eller none (kun panel, ingen Telegram).
template.mode
default (intet tilpasningsrepo til agenten — agenten kører på den rene base uden brand) eller own (partnerens tilpasningsrepo til agenten på repo_url). Ved own kloner agentens egen VPS repoet efter standard-deployet og kører dets install.sh / update.sh for at lægge din persona, dit paneltema og eventuelt ekstra værktøj oven på basen — central kører aldrig selv repoet. Peg repo_url på dit tilpasningsrepo på GitHub (HTTPS), når du vil have dit eget brand.
api.webhook_secret_prefix
De første 8 tegn af webhookens signeringshemmelighed; kun til stede, når en hemmelighed er konfigureret. Lader kalderen skelne, hvilken hemmelighed der bruges, uden at se hele værdien. Dashboardet viser det samme præfiks.
pricing.allowed_credit_options
De credit_usd-muligheder, du tilbyder dine kunder (vælges under Settings → Pricing).
readiness.missing_fields
Liste over dashboardfelter, der stadig kræves før Go Live. Tom, når ready er true. Stabil nok til at drive en ”hvad mangler stadig”-visning i din egen admin.
readiness.dns_blocker
En læsbar årsag, når tjekket af DNS-udbyderen fejler (f.eks. partner has no zone.ee username configured). Ellers null.
section_status.*
Spejler dashboardets statusmærker pr. sektion. status er ok, warn eller default (en mintbot-standard er i brug); issue angiver det felt, der blokerer sektionen (f.eks. client_apex_domain, telegram_bot_token, dns_blocker), eller null.

Eksempel

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

Listen over pakker, der kan sælges, til din butik — Brand Partner-pakkerne (mo_test / mo_month / mo_month_plus) kombineret med dine fastlagte priser, i din prisvaluta. Byg dine pakkekort ud fra den i stedet for at hardkode niveau-slugs og priser: udfasede pakker forsvinder af sig selv, og en pris- eller valutaændring, du laver i dashboardet, når din butik inden for cachevinduet — uden nyt deploy.

Det er dette endpoint, der forhindrer en butik i at reklamere for gårsdagens pakker. Referenceportalen kalder det ved hver rendering af forsiden, /buy og /extend (cachet, med en vedvarende fallback).

Visningsteksterne er dine

display_name / description / featured er mintbots kanoniske pakkemetadata. Vil du omdøbe en pakke eller omskrive beskrivelsen til dit brand, så overskriv den i din egen butik (referenceportalen har et PLAN_OVERRIDES-map netop til det) — kataloget er sandhedskilden for, hvilke pakker der findes, og hvad de koster, ikke for din markedsføringstekst.

Hent katalog

GET /api/v1/catalog

Ingen parametre. Kun Bearer-auth — der er ingen aktiveringsspærre, så en butik kan vise kataloget, før din partnerkonto er sat live.

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 }
  ]
}
Felt Betydning
currency Din prisvaluta. Alle price_cents nedenfor er angivet i den.
packages[] Én post pr. offentlig pakke, i tilbuds-/visningsrækkefølge.
tier Den slug, du sender til POST /orders / POST /subscriptions.
display_name / description / featured Kanoniske visningstekster (overskriv dem i din butik, hvis du vil).
default_credit_usd Pakkens vejledende inkluderede LLM-kredit (til orientering).
durations[] De opsætningsperioder, pakken kan købes for. months er værdien, du sender som duration_months; price_cents er det, ordren opkræver. Månedspakker tilbyder 1 / 3 / 12 måneder; mo_test tilbyder en enkelt periode på 24 timer.
subscription available er true for pakker, der kan købes som månedlig automatisk fornyelse via POST /subscriptions; price_cents er månedsbeløbet. mo_test kan aldrig købes som abonnement.
credit_options[] Hver credit_usd-mulighed prissat i din valuta (usd = kreditbeløb, price_cents = hvad den lægger oven i ordretotalen). Hvilke muligheder du tilbyder, står i pricing.allowed_credit_options i GET /settings.

Priserne svarer til det, der faktisk opkræves

Hver price_cents beregnes med den samme prislogik, som order-endpointet bruger, så en kortpris altid er lig med den endelige Stripe Checkout-total for den pågældende (tier, duration_months).

Eksempel

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-integrationer skal ofte oprette DNS-records på partnerens apex-domæne — for at pege @, www eller subdomæner pr. kunde på den rigtige server. MintOffice tilbyder en leverandøruafhængig proxy, så du kan gøre det uden nogensinde at håndtere den bagvedliggende udbyders API-nøgle i din egen infrastruktur. De legitimationsoplysninger, du gemte under onboarding (i dag zone.ee; cloudflare og route53 er på køreplanen med samme struktur), forbliver krypterede i MintOffice; dit kald kommer ind med Bearer mo_live_…, og vi foretager kaldet til udbyderen på dine vegne.

Omfanget er partnerens egen konfigurerede apex (domain.client_apex_domain fra GET /settings). Du kan ikke røre en record i en zone, du ikke ejer.

Hvis din DNS-udbyder er Manual, ændrer proxyen intet: writes gør ingenting, og lister kommer tomme tilbage.

I dag understøtter proxyen de operationer, som deploy-pipelinen selv bruger — A-records (upsert / list / slet via id). Understøttelse af CNAME / TXT / MX følger med samme struktur, når den underliggende udbyderabstraktion får dem.

Idempotency-Key er ikke nødvendig her

I modsætning til endpoints, der opretter ordrer, er DNS-upserts naturligt idempotente i udbyderlaget (at køre igen med samme (subdomain, ip) ændrer intet, og forældede dubletter ryddes op ved hvert kald). Du behøver ikke sende headeren Idempotency-Key — genforsøg ender altid med én A-record pr. FQDN.

Opret eller opdater en A-record

POST /dns/records

Peger idempotent <subdomain>.<your-apex> på en IPv4-adresse. Send "" (eller "@") for selve apex-domænet.

Request

{
  "subdomain": "www",
  "ip": "203.0.113.10"
}

subdomain accepterer:

  • "" eller "@" — selve apex-domænet (example.com).
  • En enkelt DNS-label — "www", "api" — til <label>.<apex>.
  • Punktumseparerede underlabels — "api.eu" — til indlejrede subdomæner (api.eu.example.com).

ip skal være en punktumsepareret IPv4-adresse. Hver label er [a-z0-9_-], 1–63 tegn, uden - først eller sidst.

Response — 200

{
  "apex": "example.com",
  "provider": "zone_ee",
  "fqdn": "www.example.com",
  "records": [
    { "id": "9182734", "name": "www.example.com", "destination": "203.0.113.10" }
  ]
}

Listen records efter skrivningen afspejler altid udbyderens aktuelle billede af FQDN’en efter oprydning af dubletter — så et vellykket svar med præcis én record er tegnet på, at intet forældet er tilbage.

List records

GET /dns/records

Returnerer alle A-records på den konfigurerede apex. Tilføj ?name=<label-or-fqdn> for at filtrere til én FQDN — værdien kan være en enkelt label ("www"), et punktumsepareret subdomæne ("api.eu") eller hele FQDN’en ("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" }
  ]
}

Slet en record

DELETE /dns/records/{record_id}

record_id er udbyderens id, som GET /dns/records returnerer (zone.ee bruger heltals-id’er, der laves om til strenge ved API-grænsen). Sletning af en record, der ikke længere findes, regnes som en succes — endpointet er idempotent set fra klientens side.

Response — 200

{ "deleted": true, "id": "9182734" }

Fejl

HTTP error.code Årsag
401 unauthenticated / invalid_api_key Manglende eller ugyldigt Bearer-token.
422 validation_error Ugyldigt subdomæne eller en destination, der ikke er IPv4.
422 dns_not_configured Partneren har intet client_apex_domain, eller den konfigurerede udbyders legitimationsoplysninger mangler. Ret det under Settings → Domain.
422 dns_provider_unsupported Partnerens DNS-udbyder er endnu ikke koblet på MintOffice-proxyen (f.eks. pladsholderen for cloudflare). Skift til zone_ee indtil videre.
502 dns_provider_error Udbyderen returnerede 4xx/5xx, eller forbindelsen fejlede. Fejlens message indeholder udbyderens status og et kort uddrag af svaret, renset for hemmeligheder. Sikkert at prøve igen.

Eksempel

# 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 der sker noget med en order, POSTer vi en signeret JSON event til din konfigurerede webhook URL.

Webhooks er valgfri

Webhook URL og Webhook signing secret under Settings → API access er helt valgfri — lad dem stå tomme under onboarding, hvis du ikke har en modtager endnu. Der leveres først hændelser, når begge er sat — med en URL, men uden signeringshemmelighed, sendes ingen hændelser. Dashboardet og resten af API’et fungerer fint uden webhooks; du kan i stedet polle GET /api/v1/orders/{id} for at følge livscyklussen. Tilføj URL’en (og generér hemmeligheden), når du er klar — ingen genaktivering nødvendig.

Hændelsestyper

Event Hvornår den udløses
order.created Stripe Checkout session oprettet, afventer betaling.
order.paid Stripe bekræftede betaling. Revenue event registreret.
order.cancelled Order timed out eller blev eksplicit annulleret.
agent.provisioning_started Deploy pipeline startet for denne order.
agent.ready Deploy lykkedes. Payload indeholder panel_url og expires_at.
agent.failed Deploy-pipelinen fejlede. error_step angiver det trin, der gik i stykker; error_message er et kort resumé.
agent.expired Agent TTL udløb. Partneren kan forny via POST /orders/{id}/renew.
agent.resumed En tidligere udløbet agent blev fornyet inden for sin frist og er live igen. Genoprettelses-modstykket til agent.expired.
subscription.created · subscription.activated · subscription.updated · subscription.cancelled Livscyklussen for et abonnement oprettet med POST /subscriptions.
partner.activated · partner.deactivated mintbot-teamet aktiverede din konto eller tog den af live (suspended i payloaden fortæller, om den blev suspenderet eller sat tilbage til onboarding).

Levering og genforsøg

  • Op til 7 forsøg efter en eksponentiel plan: 0s, 30s, 2m, 10m, 1h, 6h, 24h.
  • Efter det sidste forsøg markeres leveringen som exhausted, og retries stopper.
  • Svar med en 2xx status inden for 10 sekunder for at kvittere.

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
}

Verificering af signaturen

Signaturen er HMAC-SHA256(secret, "{timestamp}.{raw_body}"). Verificér altid den rå request body — hvis du serialiserer din parsede JSON igen, ødelægger det sammenligningen.

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"),
  );
}

Idempotente receivers

Brug X-Mintbot-Event-Id som din dedup key — retries genbruger samme id, så en row-level INSERT … ON CONFLICT DO NOTHING på den kolonne holder din handler sikker.

Referenceportal

En kørbar end-to-end-reference ligger på mintbot-ai/partner-portal-example — FastAPI + SQLite i Docker, med et eksempelbrand kaldet ExampleAI, som du forker og skifter ud. Arkitekturbilledet — hvordan butikken, dette API, deploy-workeren og tilpasningsrepoet hænger sammen — findes på MintOffice — Teknisk. Portalen indeholder:

  • forside, pakkevalg, takkeside og annulleringsside,
  • POST /buy → POST /api/v1/orders → omdirigering til Stripe,
  • POST /webhooks/mintoffice med HMAC-verifikationen ovenfor,
  • en hændelsesbrowser på /admin bag HTTP Basic-auth, så du hurtigt kan kigge leveringerne igennem.

Fork den, sæt PARTNER_BRAND, MINTOFFICE_API_KEY og MINTOFFICE_WEBHOOK_SECRET i .env, kør docker compose up, og peg din partnerkontos webhook-URL på https://<your-host>/webhooks/mintoffice. Hele gennemgangen står i MintOffice → Referenceportal.


Fejl

Hvert error response har en stabil code, som du kan branch'e på programmatisk. message feltet er et menneskelæsbart hint og kan ændre sig mellem releases. request_id gentager X-Request-Id response headeren — medtag det i support tickets.

{
  "error": {
    "code": "invalid_api_key",
    "message": "API key is unknown or revoked.",
    "request_id": "f0c2d6c4-…"
  }
}
Code Betydning
unauthenticated Manglende eller forkert formet Authorization header.
invalid_api_key API key er ukendt eller er roteret ud.
rate_limited Rate limit pr. partner eller pr. IP overskredet — se Retry-After.
missing_idempotency_key POST request uden Idempotency-Key.
idempotency_key_mismatch Samme key genbrugt med en anden request body.
validation_error Request body bestod ikke schema validation.
not_found Ressource findes ikke eller tilhører ikke din partner.
partner_not_live HTTP 403 — en write (POST /orders, /orders/{id}/renew, /subscriptions), mens din konto ikke er live. Body indeholder status og, for konti i onboarding, missing_fields + dns_blocker.
conflict HTTP 409 — en request med samme Idempotency-Key er stadig under behandling. Prøv igen efter Retry-After.
unpriced_tier HTTP 409 — niveauet har ingen pris at opkræve.
internal_error Uventet fejl på serversiden. Medtag request_id, når du kontakter support.
payment_gateway_error Stripe afviste oprettelsen af Checkout session.
dns_not_configured POST /dns/records mod en partner uden apex eller uden DNS-legitimationsoplysninger. Ret det under Settings → Domain.
dns_provider_unsupported Partnerens dns_provider-værdi er endnu ikke koblet på MintOffice-proxyen.
dns_provider_error DNS-udbyderen returnerede en fejl — beskeden indeholder et uddrag renset for hemmeligheder.
quota_exceeded HTTP 429, kun i DEV-miljøet (mint.mintbot.dev) — LIVE har ingen grænse pr. partner. Partneren har brugt alle deploy-pladser (10 som standard, 20 efter Brand Partner-opstartsgebyret). Kontakt mintbots support for at hæve grænsen. Returneres af POST /orders og POST /orders/{id}/renew. Body indeholder deploys_used + deploy_quota.

Rate limits

  • 120 requests / 60 s rolling window, pr. partner. Burst og steady-state deler samme bucket.
  • 60 requests / 60 s separat per-IP bucket for unauthenticated og failed-auth requests — forhindrer bearer brute-force i at æde af partnerens kvote.
  • Overskydende requests returnerer 429 rate_limited med en Retry-After header (sekunder at vente).

Brug for hjælp?

Denne dokumentation er skrevet til de partnere, der faktisk bruger API'et. Hvis noget mangler, er forvirrende eller forældet, så nævn det for din mintbot agent — de videresender feedbacken, og vi opdaterer siden.