Aller au contenu

Brand Partner API

Une petite API REST prévisible pour créer des commandes mintbot, consulter leur statut et recevoir des événements de cycle de vie. JSON en entrée, JSON en sortie. Authentification par bearer token. Écritures idempotentes. Webhooks signés.

Dashboard d’accès API Aller aux webhooks


En bref

Base URL https://mint.mintbot.ai/api/v1
Auth `Authorization: Bearer ***
Idempotency Idempotency-Key: <uuid> sur chaque POST
Content type application/json
Rate limit 120 req / 60 s par partenaire
Webhooks Signés avec HMAC-SHA256, réessayés jusqu’à 7 fois

Authentification

Chaque requête envoie une clé API partenaire dans l’en-tête Authorization. Génère ou fais tourner la clé dans le dashboard.

Authorization: Bearer mo_liv...xxxx
Content-Type: application/json

La rotation n’a pas de fenêtre de grâce

Faire tourner la clé révoque atomiquement la précédente. Prévois de remplacer la valeur dans ta config avant de cliquer sur Rotate.

Idempotence

Chaque POST requiert un en-tête Idempotency-Key. N’importe quel UUID convient pour chaque requête distincte.

  • Nous mettons la réponse en cache pendant 24 heures. Une nouvelle tentative avec la même clé rejoue la réponse d’origine avec Idempotent-Replay: true.
  • Réutiliser la même clé avec un corps différent renvoie 409 idempotency_key_mismatch.

Commandes

Créer une commande

POST /orders

Crée une commande et renvoie une URL Stripe Checkout. Une fois le paiement confirmé, mintbot provisionne l’agent et l’événement de revenu correspondant à la part partenaire est enregistré automatiquement.

Requête

{
  "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
Une des valeurs trial, s1, s2, s4.
duration_months
Durée de vie du serveur en mois calendaires. Doit être 1, 3 ou 12.
credit_usd
Optional. Crédit de chat préfinancé inclus avec la commande.
language
Optional. Influence la locale de Stripe Checkout et la langue de l’e-mail de bienvenue.
external_id
Optional. Renvoyé dans chaque webhook et chaque réponse de commande — utilise-le pour relier les commandes mintbot aux lignes de ton propre système.
success_url · cancel_url
URL de retour Stripe Checkout. {ORDER_ID} est remplacé côté serveur.
webhook_url
Optional. Remplacement, pour cette commande, de l’URL de webhook configurée au niveau partenaire.

Réponse — 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
}

Exemple

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

Récupérer une commande

GET /orders/{id}

Récupère une commande unique à partir de son id mintbot. Renvoie la même forme que POST /orders.

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

Lister les commandes

GET /orders

Liste paginée par curseur, des plus récentes aux plus anciennes.

Paramètres de requête

status
Optional. Filtrer par awaiting_payment, completed, deployed, deploy_failed ou expired.
cursor
Optional. Passe next_cursor depuis la page précédente pour continuer.

Réponse

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

next_cursor vaut null quand il n’y a plus de pages.


Renouveler une commande

POST /orders/{id}/renew

Prolonge un agent existant de duration_months supplémentaires. Infra uniquement — aucun nouveau crédit de chat n’est inclus. Renvoie un nouvel id de commande et une nouvelle URL Stripe Checkout.

Requête

{
  "duration_months": 1,
  "external_id": "your-side-id",
  "success_url": "https://your.app/thanks?id={ORDER_ID}",
  "cancel_url":  "https://your.app/account"
}

Revenus

Lire les revenus

GET /revenue

Totaux plus les 200 derniers événements du ledger.

Paramètres de requête

include_paid
Optional, default true. Mets à false pour voir uniquement les événements qui n’ont pas encore été payés.

Réponse

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

Profil partenaire

Récupérer le profil

GET /partner

Renvoie ton profil partenaire ainsi que le solde impayé — pratique pour afficher les gains dans ta propre interface d’administration sans les stocker toi-même.

Réponse

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

Quand quelque chose arrive à une commande, nous envoyons par POST un événement JSON signé à ton URL de webhook configurée.

Types d’événements

Événement Quand il se déclenche
order.created Session Stripe Checkout créée, en attente de paiement.
order.paid Paiement confirmé par Stripe. Événement de revenu enregistré.
order.cancelled Commande expirée ou explicitement annulée.
agent.provisioning_started Pipeline de déploiement démarré pour cette commande.
agent.ready Déploiement réussi. Le payload contient panel_url et expires_at.
agent.failed Erreur dans le pipeline de déploiement. Consulte le champ error pour l’étape qui a cassé.
agent.expired TTL de l’agent écoulé. Le partenaire peut renouveler via POST /orders/{id}/renew.

Livraison et nouvelles tentatives

  • Jusqu’à 7 tentatives selon un calendrier exponentiel : 0s, 30s, 2m, 10m, 1h, 6h, 24h.
  • Après la dernière tentative, la livraison est marquée exhausted et les nouvelles tentatives s’arrêtent.
  • Réponds avec un statut 2xx sous 10 secondes pour acquitter.

En-têtes de requête

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

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

Vérifier la signature

La signature est HMAC-SHA256(secret, "{timestamp}.{raw_body}"). Vérifie toujours le corps de requête brut — resérialiser le JSON déjà parsé cassera la comparaison.

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

Récepteurs idempotents

Utilise X-Mintbot-Event-Id comme clé de déduplication — les nouvelles tentatives réutilisent le même id, donc un INSERT … ON CONFLICT DO NOTHING au niveau ligne sur cette colonne garde ton handler sûr.


Erreurs

Chaque réponse d’erreur porte un code stable sur lequel tu peux brancher ton code. Le champ message est une indication lisible par un humain et peut changer entre les versions. request_id reprend l’en-tête de réponse X-Request-Id — inclus-le dans les tickets de support.

{
  "error": {
    "code": "invalid_api_key",
    "message": "API key is unknown or revoked.",
    "request_id": "f0c2d6c4-…"
  }
}
Code Signification
unauthenticated En-tête Authorization manquant ou mal formé.
invalid_api_key La clé API est inconnue ou a été remplacée par rotation.
rate_limited Limite de débit par partenaire ou par IP dépassée — voir Retry-After.
missing_idempotency_key Requête POST sans Idempotency-Key.
idempotency_key_mismatch Même clé réutilisée avec un corps de requête différent.
validation_error Le corps de la requête a échoué à la validation du schéma.
not_found La ressource n’existe pas ou n’appartient pas à ton partenaire.
payment_gateway_error Stripe a rejeté la création de la session Checkout.

Limites de débit

  • 120 requêtes / fenêtre glissante de 60 s, par partenaire. Les bursts et le débit stable partagent le même bucket.
  • 60 requêtes / 60 s dans un bucket séparé par IP pour les requêtes non authentifiées et les échecs d’authentification — cela empêche une attaque brute-force bearer de consommer le quota partenaire.
  • Les requêtes en trop renvoient 429 rate_limited avec un en-tête Retry-After (secondes d’attente).

Besoin d’aide ?

Cette documentation est écrite pour les partenaires qui utilisent réellement l’API. Si quelque chose manque, prête à confusion ou n’est plus à jour, signale-le à ton agent mintbot — il transmettra le retour et nous mettrons la page à jour.