Brand Partner API¶
Eine kleine, berechenbare REST API zum Erstellen von mintbot-Bestellungen, zum Abfragen ihres Status und zum Empfangen von Lifecycle-Ereignissen. JSON rein, JSON raus. Bearer-token auth. Idempotente Schreibvorgänge. Signierte Webhooks.
Dashboard für API-Zugriff Zu Webhooks springen
Auf einen Blick¶
| Base URL | https://mint.mintbot.ai/api/v1 |
| Auth | `Authorization: Bearer *** |
| Idempotency | Idempotency-Key: <uuid> bei jedem POST |
| Content type | application/json |
| Rate limit | 120 req / 60 s pro Partner |
| Webhooks | Signiert mit HMAC-SHA256, bis zu 7 Wiederholungsversuche |
Authentifizierung¶
Jede Anfrage sendet einen Partner-API-key im Authorization header. Erstelle oder rotiere den key im Dashboard.
Authorization: Bearer mo_liv...xxxx
Content-Type: application/json
Rotation hat kein Karenzfenster
Das Rotieren des keys widerruft den vorherigen key atomar. Plane, den Wert in deiner Konfiguration zu tauschen, bevor du auf Rotate klickst.
Idempotenz¶
Jeder POST benötigt einen Idempotency-Key header. Jede UUID pro eindeutiger Anfrage funktioniert.
- Wir cachen die Antwort für 24 Stunden. Ein erneuter Versuch mit demselben key spielt die ursprüngliche Antwort mit
Idempotent-Replay: trueerneut aus. - Wenn du denselben key mit einem anderen body wiederverwendest, kommt
409 idempotency_key_mismatchzurück.
Bestellungen¶
Bestellung erstellen¶
POST /orders
Erstellt eine Bestellung und gibt eine Stripe Checkout URL zurück. Sobald die Zahlung bestätigt ist, provisioniert mintbot den Agenten und das Umsatzereignis für den Partneranteil wird automatisch aufgezeichnet.
Anfrage
{
"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- Einer von
trial,s1,s2,s4. duration_months- Kalendermonate Serverlaufzeit. Muss einer von
1,3oder12sein. credit_usd- Optional. Vorab finanziertes Chat-Guthaben, das mit der Bestellung gebündelt ist.
language- Optional. Beeinflusst die Stripe Checkout locale und die Sprache der Willkommens-E-Mail.
external_id- Optional. Wird in jedem Webhook und jeder Bestellantwort zurückgegeben — nutze es, um mintbot-Bestellungen mit Zeilen in deinem eigenen System zu verknüpfen.
success_url·cancel_url- Rückkehr-URLs für Stripe Checkout.
{ORDER_ID}wird serverseitig ersetzt. webhook_url- Optional. Bestellspezifischer Override für die Webhook-URL auf Partnerebene.
Antwort — 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
}
Beispiel
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"]
Bestellung abrufen¶
GET /orders/{id}
Ruft eine einzelne Bestellung anhand ihrer mintbot id ab. Gibt dieselbe Struktur wie POST /orders zurück.
curl https://mint.mintbot.ai/api/v1/orders/42 \
-H "Authorization: Bearer ***
Bestellungen auflisten¶
GET /orders
Cursor-paginierte Liste, neueste zuerst.
Query parameters
status- Optional. Filtere nach
awaiting_payment,completed,deployed,deploy_failedoderexpired. cursor- Optional. Übergib
next_cursorvon der vorherigen Seite, um fortzufahren.
Antwort
{
"items": [ /* OrderResponse … */ ],
"next_cursor": "37"
}
next_cursor ist null, wenn es keine weiteren Seiten gibt.
Bestellung verlängern¶
POST /orders/{id}/renew
Verlängert einen bestehenden Agenten um weitere duration_months. Nur Infra — es wird kein neues Chat-Guthaben gebündelt. Gibt eine neue Bestell-id und eine frische Stripe Checkout URL zurück.
Anfrage
{
"duration_months": 1,
"external_id": "your-side-id",
"success_url": "https://your.app/thanks?id={ORDER_ID}",
"cancel_url": "https://your.app/account"
}
Umsatz¶
Umsatz lesen¶
GET /revenue
Summen plus die neuesten 200 Ledger-Ereignisse.
Query parameters
include_paid- Optional, default
true. Setze auffalse, um nur Ereignisse zu sehen, die noch nicht ausgezahlt wurden.
Antwort
{
"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¶
Profil abrufen¶
GET /partner
Gibt dein Partnerprofil plus den nicht ausgezahlten Saldo zurück — praktisch, um Einnahmen in deiner eigenen Admin UI anzuzeigen, ohne sie selbst zu speichern.
Antwort
{
"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¶
Wenn etwas mit einer Bestellung passiert, senden wir ein signiertes JSON-Ereignis per POST an deine konfigurierte Webhook URL.
Ereignistypen¶
| Event | Wann es ausgelöst wird |
|---|---|
order.created |
Stripe Checkout session wurde erstellt, Zahlung ausstehend. |
order.paid |
Stripe hat die Zahlung bestätigt. Umsatzereignis wurde aufgezeichnet. |
order.cancelled |
Bestellung ist abgelaufen oder wurde ausdrücklich storniert. |
agent.provisioning_started |
Deploy pipeline für diese Bestellung wurde gestartet. |
agent.ready |
Deploy erfolgreich. Payload enthält panel_url und expires_at. |
agent.failed |
Deploy pipeline ist fehlgeschlagen. Prüfe das Feld error für den defekten Schritt. |
agent.expired |
Agent TTL ist abgelaufen. Der Partner kann über POST /orders/{id}/renew verlängern. |
Zustellung & Wiederholungsversuche¶
- Bis zu 7 Versuche nach exponentiellem Zeitplan:
0s, 30s, 2m, 10m, 1h, 6h, 24h. - Nach dem letzten Versuch wird die Zustellung als
exhaustedmarkiert und nicht weiter wiederholt. - Antworte innerhalb von 10 Sekunden mit einem
2xxstatus, um zu bestätigen.
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
Beispiel-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"
}
Signatur verifizieren¶
Die Signatur ist HMAC-SHA256(secret, "{timestamp}.{raw_body}"). Verifiziere immer den rohen request body — wenn du dein geparstes JSON erneut serialisierst, bricht der Vergleich.
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 Empfänger
Nutze X-Mintbot-Event-Id als deinen Dedupe-key — Wiederholungsversuche verwenden dieselbe id wieder, sodass ein zeilenweises INSERT … ON CONFLICT DO NOTHING auf dieser Spalte deinen Handler sicher hält.
Fehler¶
Jede Fehlerantwort enthält einen stabilen code, nach dem du programmatisch verzweigen kannst. Das Feld message ist ein menschenlesbarer Hinweis und kann sich zwischen Releases ändern. request_id spiegelt den X-Request-Id response header wider — füge ihn Support-Tickets hinzu.
{
"error": {
"code": "invalid_api_key",
"message": "API key is unknown or revoked.",
"request_id": "f0c2d6c4-…"
}
}
| Code | Bedeutung |
|---|---|
unauthenticated |
Fehlender oder fehlerhafter Authorization header. |
invalid_api_key |
API key ist unbekannt oder wurde herausrotiert. |
rate_limited |
Rate limit pro Partner oder pro IP überschritten — siehe Retry-After. |
missing_idempotency_key |
POST request ohne Idempotency-Key. |
idempotency_key_mismatch |
Derselbe key wurde mit einem anderen request body wiederverwendet. |
validation_error |
Request body hat die Schema-Validierung nicht bestanden. |
not_found |
Ressource existiert nicht oder gehört nicht zu deinem Partner. |
payment_gateway_error |
Stripe hat die Erstellung der Checkout session abgelehnt. |
Rate limits¶
- 120 requests / 60 s rolling window, pro Partner. Burst und steady-state teilen sich denselben bucket.
- 60 requests / 60 s separater bucket pro IP für nicht authentifizierte und fehlgeschlagene Auth-Anfragen — verhindert, dass ein Bearer brute-force die Partnerquote aufbraucht.
- Überschüssige Anfragen geben
429 rate_limitedmit einemRetry-Afterheader zurück (Sekunden zum Zurückziehen).
Brauchst du Hilfe?¶
Diese Dokumentation ist für Partner geschrieben, die die API wirklich nutzen. Wenn etwas fehlt, unklar oder veraltet ist, erwähne es gegenüber deinem mintbot-Agenten — er leitet das Feedback weiter und wir aktualisieren die Seite.