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

Configuración

Obtener configuración

GET /settings

Devuelve tu configuración completa de partner no secreta en un solo payload — modos elegidos, dominio apex, proveedor de DNS, proveedor de bot, repositorio de plantilla, patrón de subdominios (con una etiqueta de vista previa renderizada), precios por nivel, metadatos de la clave de API, metadatos del webhook, y el mismo estado de preparación / por sección que impulsa el banner del panel de MintOffice.

Diseñado para agentes de integración que necesitan descubrir cómo está configurado el partner sin scrapear el panel ni interrogar al operador. Un llamador típico es un agente de programación que instala tu configuración de marca blanca en un servidor nuevo — puede leer este endpoint, ramificar según readiness.status, y decirle al operador exactamente qué secciones aún necesitan atención.

Nunca se devuelve ningún secreto

Cualquier cosa que el partner haya proporcionado como credencial — token del bot de Telegram, clave de API de Zone.ee, secreto de firma de webhook, el propio texto plano de la clave de API — aparece solo como un booleano *_set. El secreto de webhook expone además el mismo prefijo de visualización de 8 caracteres que ya se muestra en el panel, así que un llamador puede confirmar cuál secreto está configurado sin ver nunca el valor.

Respuesta

{
  "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": "byo",
    "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": "natural",
    "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": {
      "trial": { "price_cents": 0,    "partner_cut_cents": 0,   "updated_at": null },
      "s1":    { "price_cents": 1200, "partner_cut_cents": 200, "updated_at": "2026-05-13 09:05:00" },
      "s2":    { "price_cents": 2400, "partner_cut_cents": 400, "updated_at": "2026-05-13 09:05:00" },
      "s4":    { "price_cents": 4800, "partner_cut_cents": 800, "updated_at": "2026-05-13 09:05:00" }
    }
  },

  "readiness": {
    "ready": true,
    "missing_fields": [],
    "dns_blocker": null,
    "status": "live"
  },

  "section_status": {
    "domain":   { "status": "ready", "mode": "byo",     "issue": null, "dns_blocker": null },
    "bot":      { "status": "ready", "mode": "own",     "issue": null, "dns_blocker": null },
    "template": { "status": "ready", "mode": "default", "issue": null, "dns_blocker": null },
    "api":      { "status": "ready", "mode": "live",    "issue": null, "dns_blocker": null },
    "all_ready": true
  }
}

Referencia de campos

status
Ciclo de vida del partner. Uno de onboarding, live, paused. La Brand Partner API rechaza escrituras mientras está en onboarding.
domain.mode
byo (traes tu propio dominio apex) o hosted (mintbot provee un subdominio *.mintbot.ai). Cuando es hosted, los campos del proveedor de DNS están intencionalmente vacíos sin importar lo que el partner haya introducido antes.
domain.subdomain_label_preview
La etiqueta renderizada que recibiría el primer agente de cliente (agent1, 1, 001, …) dados los subdomain_prefix, subdomain_numbering y subdomain_pad_width actuales. Úsala para una vista previa de "esto es lo que verán tus clientes" sin reimplementar las reglas de numeración.
bot.mode
own (bot de Telegram proporcionado por el partner), borrow (usando el bot de mintbot durante las pruebas), o web (solo panel, sin Telegram).
template.mode
default (sin agent customization repo — el agente corre sobre la base sencilla y sin marca) o custom (el agent customization repo del partner en repo_url). Cuando es custom, el propio VPS del agente clona ese repositorio después del despliegue estándar y ejecuta su install.sh / update.sh para aplicar tu persona, el tema del panel y cualquier herramienta extra encima de la base — central nunca ejecuta el repositorio en sí. Apunta repo_url a tu agent customization repo HTTPS de GitHub cuando quieras una marca personalizada.
api.webhook_secret_prefix
Primeros 8 caracteres del secreto de firma de webhook, presente solo cuando hay un secreto configurado. Permite a un llamador distinguir cuál secreto está en uso sin ver el valor completo. El panel muestra el mismo prefijo.
readiness.missing_fields
Lista de nombres de campos del panel aún requeridos antes de Go Live. Vacía cuando ready es true. Lo bastante estable como para impulsar una UI de "qué falta aún" en tu propio panel de administración.
readiness.dns_blocker
Se establece a una cadena de razón corta cuando la verificación de DNS falla (p. ej. nameservers_not_pointing, glue_record_missing). null en caso contrario.
section_status.*.issue
Pista legible para humanos por sección cuando esa sección bloquea Go Live. Refleja los pills de estado por sección del panel.

Ejemplo

curl https://mint.mintbot.ai/api/v1/settings \
  -H "Authorization: Bearer ***
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"])

La lista de paquetes vendibles para tu tienda — los paquetes públicos (trial / starter / pro) unidos con tu precio resuelto, en tu moneda de precios. Renderiza tus tarjetas de plan a partir de esto en lugar de fijar a mano los slugs de nivel y los precios: los paquetes retirados desaparecen por sí solos, y un cambio de precio o moneda que hagas en el panel llega a tu tienda dentro de su ventana de caché — sin redesplegar.

Este es el endpoint que evita que una tienda anuncie los paquetes de ayer. El portal de referencia lo llama en cada render de landing / /buy / /extend (cacheado, con un fallback persistente).

Los textos de visualización siguen siendo tuyos

display_name / description / featured son los metadatos canónicos de los paquetes de mintbot. Si quieres renombrar un paquete o reescribir su descripción para tu marca, sobrescríbelo en tu propia tienda (el portal de referencia incluye un mapa PLAN_OVERRIDES exactamente para esto) — el catálogo es la fuente de verdad de qué paquetes existen y cuánto cuestan, no de tu copy de marketing.

GET /api/v1/catalog

Sin parámetros. Solo autenticación con bearer — no hay puerta de activación, así que una tienda puede renderizar el catálogo antes de que tu cuenta de partner pase a live.

Respuesta

{
  "currency": "usd",
  "packages": [
    {
      "tier": "trial",
      "display_name": "Trial",
      "description": "One day to kick the tires.",
      "featured": false,
      "default_credit_usd": 5,
      "durations": [
        { "months": 1, "label": "24 hours", "price_cents": 0 }
      ],
      "subscription": { "available": false, "price_cents": null }
    },
    {
      "tier": "starter",
      "display_name": "Starter",
      "description": "A month of assistant time.",
      "featured": true,
      "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 }
    }
  ]
}
Campo Significado
currency Tu moneda de precios. Cada price_cents de abajo está denominado en ella.
packages[] Una entrada por paquete público, en orden de oferta/visualización.
tier El slug que pasas a POST /orders / POST /subscriptions.
display_name / description / featured Copy de visualización canónico (sobrescríbelo en tu tienda si quieres).
default_credit_usd La pista de crédito LLM incluido del paquete (informativo).
durations[] Las duraciones de configuración para las que se puede comprar este paquete. months es el valor que envías como duration_months; price_cents es lo que el pedido facturará. Los paquetes mensuales anuncian 1 / 3 / 12 meses; trial anuncia una única duración de 24 horas.
subscription available es true para los paquetes que pueden comprarse como auto-renovación mensual vía POST /subscriptions; price_cents es el importe mensual. trial nunca admite suscripción.

Los precios coinciden con lo que se te facturará

Cada price_cents se calcula con el mismo resolutor de precios que usa el endpoint de pedidos, así que el precio de una tarjeta siempre equivale al total final de Stripe Checkout para ese (tier, duration_months).

Ejemplo

curl -s https://mint.mintbot.ai/api/v1/catalog \
  -H "Authorization: Bearer *** | 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"])

Registros DNS

Las integraciones de brand partner a menudo necesitan provisionar registros DNS en el dominio apex del partner — apuntando @, www, o subdominios por cliente al servidor correcto. MintOffice expone un proxy ciego al proveedor para que puedas hacer esto sin manejar nunca la clave de API del proveedor upstream en tu propia infraestructura. Las credenciales que guardaste durante el onboarding (hoy: zone.ee; cloudflare y route53 están en la hoja de ruta detrás de la misma forma) permanecen cifradas en MintOffice; tu llamada llega con Bearer mo_live_… y hacemos la llamada upstream en tu nombre.

El alcance es el propio apex configurado del partner (domain.client_apex_domain de GET /settings). No puedes tocar un registro en una zona que no te pertenece.

Hoy el proxy admite las operaciones que usa el propio pipeline de despliegue — registros A (upsert / list / delete por id). El soporte de CNAME / TXT / MX seguirá la misma forma cuando la abstracción del proveedor subyacente los incorpore.

Aquí no se requiere Idempotency-Key

A diferencia de los endpoints de creación de pedidos, los upserts de DNS son naturalmente idempotentes a nivel de proveedor (reejecutar con el mismo (subdomain, ip) es un no-op y los duplicados obsoletos se barren en cada llamada). No necesitas enviar el header Idempotency-Key — los reintentos siempre convergen en un único registro A por FQDN.

Insertar o actualizar un registro A

POST /dns/records

Apunta de forma idempotente <subdomain>.<your-apex> a una dirección IPv4. Pasa "" (o "@") para el propio apex.

Solicitud

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

subdomain acepta:

  • "" o "@" — el propio apex (example.com).
  • Una sola etiqueta DNS — "www", "api" — para <label>.<apex>.
  • Sub-etiquetas con puntos — "api.eu" — para subdominios anidados (api.eu.example.com).

ip debe ser una dirección IPv4 con puntos. Cada etiqueta es [a-z0-9_-], 1–63 caracteres, sin - al principio ni al final.

Respuesta — 200

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

La lista records posterior a la escritura siempre refleja la vista actual del proveedor upstream del FQDN tras un barrido de duplicados — así que una respuesta exitosa con exactamente un registro es la señal de camino feliz de que no queda nada obsoleto.

Listar registros

GET /dns/records

Devuelve cada registro A del apex configurado. Añade ?name=<label-or-fqdn> para filtrar a un solo FQDN — el valor puede ser una etiqueta desnuda ("www"), un subdominio con puntos ("api.eu"), o el FQDN completo ("www.example.com").

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

Eliminar un registro

DELETE /dns/records/{record_id}

record_id es el identificador del proveedor devuelto por GET /dns/records (zone.ee emite ids enteros, convertidos a string en la frontera de la API). Eliminar un registro que ya no existe se trata como un éxito — el endpoint es idempotente del lado del cliente.

Respuesta — 200

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

Errores

HTTP error.code Causa
401 unauthenticated / invalid_api_key Token Bearer ausente / incorrecto.
422 validation_error Subdominio incorrecto o destino no IPv4.
422 dns_not_configured El partner no tiene client_apex_domain o faltan las credenciales del proveedor configurado. Corrígelo en Settings → Domain.
422 dns_provider_unsupported El proveedor de DNS del partner aún no está conectado al proxy de MintOffice (p. ej. el placeholder de cloudflare). Cámbiate a zone_ee por ahora.
502 dns_provider_error El proveedor upstream devolvió un 4xx/5xx, o el transporte falló. El message del error lleva el estado upstream y un extracto corto, con secretos redactados, del cuerpo upstream. Seguro de reintentar.

Ejemplo

# Point apex + www at your server's IP.
curl https://mint.mintbot.ai/api/v1/dns/records \
  -H "Authorization: Bearer *** \
  -H "Content-Type: application/json" \
  -d '{"subdomain": "",    "ip": "203.0.113.10"}'

curl https://mint.mintbot.ai/api/v1/dns/records \
  -H "Authorization: Bearer *** \
  -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 ***
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

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.