Saltar a contenido

Brand Partner API

Una REST API pequeña y predecible para crear pedidos de mintbot, consultar su estado y recibir eventos de ciclo de vida. JSON entra, JSON sale. Autenticación con bearer token. Escrituras idempotentes. Webhooks firmados.

Panel de acceso a la API Saltar a Webhooks


De un vistazo

URL base https://mint.mintbot.ai/api/v1
Autenticación `Authorization: Bearer ***
Idempotencia Idempotency-Key: <uuid> en cada POST
Tipo de contenido application/json
Rate limit 120 req / 60 s por partner
Webhooks Firmados con HMAC-SHA256, reintentados hasta 7 veces

Autenticación

Cada solicitud envía una clave de API de partner en el header Authorization. Genera o rota la clave en el dashboard.

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

La rotación no tiene ventana de gracia

Rotar la clave revoca la anterior de forma atómica. Planifica cambiar el valor en tu configuración antes de hacer clic en Rotate.

Idempotencia

Cada POST requiere un header Idempotency-Key. Sirve cualquier UUID por cada solicitud distinta.

  • Guardamos la respuesta en caché durante 24 horas. Un reintento con la misma clave reproduce la respuesta original con Idempotent-Replay: true.
  • Reutilizar la misma clave con un body diferente devuelve 409 idempotency_key_mismatch.

Pedidos

Crear pedido

POST /orders

Crea un pedido y devuelve una URL de Stripe Checkout. Cuando se confirma el pago, mintbot provisiona el agente y el evento de ingresos correspondiente a la parte del partner se registra automáticamente.

Solicitud

{
  "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
Uno de trial, s1, s2, s4.
duration_months
Meses calendario de vida útil del servidor. Debe ser uno de 1, 3 o 12.
credit_usd
Opcional. Crédito de chat prefinanciado incluido con el pedido.
language
Opcional. Afecta al locale de Stripe Checkout y al idioma del email de bienvenida.
external_id
Opcional. Se devuelve en cada webhook y respuesta de pedido: úsalo para vincular pedidos de mintbot con filas de tu propio sistema.
success_url · cancel_url
URLs de retorno de Stripe Checkout. {ORDER_ID} se sustituye en el servidor.
webhook_url
Opcional. Sustitución por pedido de la URL de webhook configurada a nivel de partner.

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

Ejemplo

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

Obtener pedido

GET /orders/{id}

Obtiene un solo pedido por su id de mintbot. Devuelve la misma forma que POST /orders.

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

Listar pedidos

GET /orders

Lista paginada con cursor, de más reciente a más antiguo.

Parámetros de consulta

status
Opcional. Filtra por awaiting_payment, completed, deployed, deploy_failed o expired.
cursor
Opcional. Pasa el next_cursor de la página anterior para continuar.

Respuesta

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

next_cursor es null cuando no hay más páginas.


Renovar pedido

POST /orders/{id}/renew

Extiende un agente existente durante otros duration_months. Solo infraestructura: no se incluye crédito de chat nuevo. Devuelve un nuevo id de pedido y una URL nueva de Stripe Checkout.

Solicitud

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

Ingresos

Leer ingresos

GET /revenue

Totales más los 200 eventos de ledger más recientes.

Parámetros de consulta

include_paid
Opcional, por defecto true. Ponlo en false para ver solo eventos que aún no se han pagado.

Respuesta

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

Perfil del partner

Obtener perfil

GET /partner

Devuelve tu perfil de partner más el saldo no pagado: útil para mostrar ingresos dentro de tu propio panel de administración sin almacenarlos tú.

Respuesta

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

Cuando le ocurre algo a un pedido, hacemos POST de un evento JSON firmado a tu URL de webhook configurada.

Tipos de evento

Evento Cuándo se dispara
order.created Sesión de Stripe Checkout creada, esperando pago.
order.paid Stripe confirmó el pago. Evento de ingresos registrado.
order.cancelled El pedido agotó el tiempo o fue cancelado explícitamente.
agent.provisioning_started El pipeline de deploy empezó para este pedido.
agent.ready El deploy tuvo éxito. El payload contiene panel_url y expires_at.
agent.failed El pipeline de deploy falló. Revisa el campo error para ver el paso que se rompió.
agent.expired El TTL del agente terminó. El partner puede renovar vía POST /orders/{id}/renew.

Entrega y reintentos

  • Hasta 7 intentos con un calendario exponencial: 0s, 30s, 2m, 10m, 1h, 6h, 24h.
  • Tras el último intento, la entrega se marca como exhausted y deja de reintentarse.
  • Responde con un estado 2xx en un plazo de 10 segundos para confirmar la recepción.

Headers de solicitud

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

Payload de ejemplo — 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"
}

Verificar la firma

La firma es HMAC-SHA256(secret, "{timestamp}.{raw_body}"). Verifica siempre el cuerpo raw de la solicitud: volver a serializar el JSON parseado romperá la comparación.

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

Receptores idempotentes

Usa X-Mintbot-Event-Id como clave de deduplicación: los reintentos reutilizan el mismo id, así que un INSERT … ON CONFLICT DO NOTHING a nivel de fila sobre esa columna mantiene seguro tu handler.


Errores

Cada respuesta de error lleva un code estable sobre el que puedes ramificar programáticamente. El campo message es una pista legible para humanos y puede cambiar entre versiones. El request_id replica el header de respuesta X-Request-Id; inclúyelo en tickets de soporte.

{
  "error": {
    "code": "invalid_api_key",
    "message": "API key is unknown or revoked.",
    "request_id": "f0c2d6c4-…"
  }
}
Código Significado
unauthenticated Header Authorization ausente o mal formado.
invalid_api_key La API key es desconocida o fue rotada.
rate_limited Se superó el rate limit por partner o por IP; consulta Retry-After.
missing_idempotency_key Solicitud POST sin Idempotency-Key.
idempotency_key_mismatch Misma clave reutilizada con un body de solicitud diferente.
validation_error El body de la solicitud no pasó la validación del esquema.
not_found El recurso no existe o no pertenece a tu partner.
payment_gateway_error Stripe rechazó la creación de la sesión de Checkout.

Rate limits

  • 120 solicitudes / ventana móvil de 60 s, por partner. Ráfagas y estado estable comparten el mismo bucket.
  • 60 solicitudes / 60 s, bucket separado por IP para solicitudes no autenticadas y con autenticación fallida: evita que un ataque de fuerza bruta al bearer consuma la cuota del partner.
  • Las solicitudes excedentes devuelven 429 rate_limited con un header Retry-After (segundos que debes esperar).

¿Necesitas ayuda?

Esta documentación está escrita para los partners que realmente usan la API. Si algo falta, resulta confuso o está desactualizado, coméntaselo a tu agente de mintbot: nos reenviará el feedback y actualizaremos la página.