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,3o12. 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_failedoexpired. cursor- Opcional. Pasa el
next_cursorde 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 enfalsepara 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á enonboarding. domain.modebyo(traes tu propio dominio apex) ohosted(mintbot provee un subdominio*.mintbot.ai). Cuando eshosted, 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 lossubdomain_prefix,subdomain_numberingysubdomain_pad_widthactuales. Úsala para una vista previa de "esto es lo que verán tus clientes" sin reimplementar las reglas de numeración. bot.modeown(bot de Telegram proporcionado por el partner),borrow(usando el bot de mintbot durante las pruebas), oweb(solo panel, sin Telegram).template.modedefault(sin agent customization repo — el agente corre sobre la base sencilla y sin marca) ocustom(el agent customization repo del partner enrepo_url). Cuando escustom, el propio VPS del agente clona ese repositorio después del despliegue estándar y ejecuta suinstall.sh/update.shpara aplicar tu persona, el tema del panel y cualquier herramienta extra encima de la base — central nunca ejecuta el repositorio en sí. Apuntarepo_urla 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
readyestrue. 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).nullen 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"])
Catálogo¶
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.
Obtener catálogo¶
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
exhaustedy deja de reintentarse. - Responde con un estado
2xxen 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_limitedcon un headerRetry-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.