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 *** |
| Idempotency | Idempotency-Key: <uuid> på hver POST |
| 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_liv...xxxx
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. 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": "s1",
"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
trial,s1,s2,s4. duration_months- Kalendermåneder for serverens levetid. Skal være en af
1,3eller12. credit_usd- Valgfri. Forudfinansieret chat credit, der følger med ordren.
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.
Response — 201 Created
{
"id": 42,
"tier": "s1",
"duration_months": 1,
"credit_usd": 10,
"amount_cents": 1200,
"currency": "usd",
"status": "awaiting_payment",
"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
}
Eksempel
curl -X POST https://mint.mintbot.ai/api/v1/orders \
-H "Authorization: Bearer *** \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"tier": "s1",
"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": "s1",
"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 ***
List orders¶
GET /orders
Cursor-pagineret liste, nyeste først.
Query parameters
status- Valgfri. Filtrér efter
awaiting_payment,completed,deployed,deploy_failedellerexpired. cursor- Valgfri. Send
next_cursorfra den forrige side for at fortsætte.
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"
}
Revenue¶
Læs revenue¶
GET /revenue
Totaler plus de seneste 200 ledger events.
Query parameters
include_paid- Valgfri, default
true. Sæt tilfalsefor 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"
}
Webhooks¶
Når der sker noget med en order, POSTer vi en signeret JSON event til din konfigurerede webhook URL.
Event types¶
| 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 pipeline fejlede. Tjek error feltet for det trin, der gik i stykker. |
agent.expired |
Agent TTL udløb. Partneren kan forny via POST /orders/{id}/renew. |
Delivery & retries¶
- 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
2xxstatus 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": "s1",
"duration_months": 1,
"credit_usd": 10,
"amount_cents": 1200,
"currency": "usd",
"status": "completed",
"external_id": "your-side-id",
"paid_at": "2026-05-16 10:02:14"
}
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.
Errors¶
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. |
payment_gateway_error |
Stripe afviste oprettelsen af Checkout session. |
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_limitedmed enRetry-Afterheader (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.