API de Innobox

API HTTP para vender software con licencias de forma headless: integra la tienda, cobra en tu propio Stripe, entrega llaves de licencia y consulta tus métricas — todo desde tu backend o tu sitio. Pensada para promotores que quieren embeber o automatizar su tienda, y para desarrolladores que construyen apps sobre la plataforma.

URL base

https://eosxwcbciazxontwytie.supabase.co/functions/v1/

Todas las rutas de esta página son relativas a esa base. Ejemplo: POST /reseller-checkout = POST https://…/functions/v1/reseller-checkout.

Montos en centavos. Todos los precios y montos (amount, list_price, unit, revenue…) van en la unidad menor de la moneda (centavos). 150000 = $1,500.00 MXN.

Autenticación

Hay dos credenciales de promotor. Elige según cómo integres. Los endpoints de licencias y de analítica no requieren autenticación de promotor (ver sus secciones).

a) API key del promotor x-api-key

Clave permanente rk_live_… que obtienes (y rotas) desde tu panel de promotor. Ideal para integraciones servidor-a-servidor: no expira. Envíala en el header:

x-api-key: rk_live_xxxxxxxxxxxxxxxxxxxxxxxx

Sirve para reseller-api (todas las acciones) y para canjear ventas en reseller-mint (POST). Guárdala solo en tu backend; nunca la expongas en el navegador.

b) Token de sesión Bearer

Token de corta vida (24 h) que obtienes con correo + contraseña vía reseller-auth. Pensado para un dashboard/UI de promotor. Envíalo como Bearer:

Authorization: Bearer <token>
POST /reseller-auth Sin auth

Login del promotor. Verifica correo + contraseña y devuelve un token de sesión (Ed25519, válido 24 h). Tras 8 intentos fallidos bloquea la cuenta 15 minutos.

Body

CampoTipoDescripción
emailstringobligatorioCorreo del promotor.
passwordstringobligatorioContraseña del promotor.

Respuesta

{ "token": "eyJ0eXAiOiJyZXNlbGxlciIsInJpZCI6Ii4uLiJ9.SIG..." }

Ejemplo

curl -X POST https://eosxwcbciazxontwytie.supabase.co/functions/v1/reseller-auth \
  -H "Content-Type: application/json" \
  -d '{"email":"promotor@correo.com","password":"tu-contrasena"}'

2 · Vender (headless)

Flujo del promotor en dos pasos: (a) crea una sesión de Checkout en tu Stripe y redirige al comprador; (b) tras el pago, canjea la sesión para mintear/renovar la licencia. El dinero nunca pasa por Innobox — se crea en tu cuenta con la llave secreta que registraste.

POST /reseller-checkout Sin auth (por r)

Crea la sesión de Stripe Checkout en la cuenta del promotor con el precio que fijó (revalidado contra el piso general). Devuelve la URL a la que rediriges al comprador.

Body

CampoTipoDescripción
ruuidobligatorioID del promotor.
product_slugstringobligatorioSlug del producto publicado (p. ej. leadops).
buyer_emailstringopcionalCorreo del comprador (prellena el Checkout y se usa para la entrega).
renew_keystringopcionalSi viene, es una renovación (+1 año) de esa clave existente en lugar de una venta nueva.
utmobjectopcionalAtribución: utm_source/medium/campaign/content/term. Se propaga a la venta.

La tienda del promotor es de precio único: reseller-checkout no recibe plan (los niveles Individual/Equipo/Agencia son concepto de la matriz).

Respuesta

{ "url": "https://checkout.stripe.com/c/pay/cs_live_..." }

Ejemplo

curl -X POST https://eosxwcbciazxontwytie.supabase.co/functions/v1/reseller-checkout \
  -H "Content-Type: application/json" \
  -d '{"r":"RESELLER_ID","product_slug":"leadops","buyer_email":"cliente@correo.com"}'

Entregar / canjear la licencia

Tras el pago, Stripe redirige a tu success_url con session_id. Dispones de dos caminos para materializar la licencia. Además, el webhook que reseller-api → stripe-setup creó en tu cuenta mintea automáticamente en segundo plano (idempotente), así que el canje manual nunca duplica.

delivery_mode. auto_email: Innobox envía la clave por correo al comprador (con tu marca) en cuanto paga. self: tú entregas la clave; la ves en tu panel / respuesta del canje. Configúralo con reseller-api → set-delivery.
GET /reseller-mint?r=<id>&session_id=<sid> Sin auth (página de éxito)

Camino de la página pública de éxito: re-consulta la sesión con la sk del promotor, mintea si procede y responde según el delivery_mode. Si el pago aún no se confirma, devuelve pending.

Respuestas posibles

// venta, modo auto_email
{ "ok": true, "delivered": "auto_email", "key": "LO-XXXX-XXXX-XXXX-XXXX" }

// venta, modo self (no revela la clave al comprador)
{ "ok": true, "delivered": "self", "message": "El vendedor te enviará tu clave" }

// renovación
{ "ok": true, "renewal": true }

// pago aún en proceso
{ "ok": false, "pending": true, "error": "Pago en proceso" }

Ejemplo

curl "https://eosxwcbciazxontwytie.supabase.co/functions/v1/reseller-mint?r=RESELLER_ID&session_id=cs_live_..."
POST /reseller-mint?r=<id> x-api-key

Camino headless: autenticado con tu API key, siempre devuelve la clave para que tú la entregues (independiente del delivery_mode). Idempotente por sesión.

Body

CampoTipoDescripción
session_idstringobligatorioID de la sesión de Checkout pagada.

Respuesta

{ "ok": true, "key": "LO-XXXX-XXXX-XXXX-XXXX", "minted": true, "kind": "sale" }

Ejemplo

curl -X POST "https://eosxwcbciazxontwytie.supabase.co/functions/v1/reseller-mint?r=RESELLER_ID" \
  -H "x-api-key: rk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"session_id":"cs_live_..."}'

3 · Venta directa (matriz)

Venta directa de Innobox (nuestro Stripe, precio de lista). Soporta niveles/planes, cupones de lanzamiento y renovación. La entrega la hacen el webhook y la página de éxito de la matriz automáticamente.

POST /store-checkout Sin auth

Crea la sesión de Checkout en el Stripe de la matriz y devuelve la URL de pago.

Body

CampoTipoDescripción
product_slugstringobligatorioSlug del producto publicado.
planstringopcionalClave del nivel del producto (p. ej. individual, equipo). Sin él se usa el primer plan. Solo aplica a ventas nuevas.
couponstringopcionalCódigo de cupón/promoción a pre-aplicar (solo venta nueva). Si no se pasa, el Checkout permite teclear código.
renew_keystringopcionalRenovación (+1 año) de esa clave en lugar de venta nueva.
buyer_emailstringopcionalCorreo del comprador (prellena el Checkout).
utmobjectopcionalAtribución utm_*.
testbooleanopcionalUsa la llave de Stripe en modo prueba.

Respuesta

{ "url": "https://checkout.stripe.com/c/pay/cs_live_..." }

Ejemplo

curl -X POST https://eosxwcbciazxontwytie.supabase.co/functions/v1/store-checkout \
  -H "Content-Type: application/json" \
  -d '{"product_slug":"leadops","plan":"individual","buyer_email":"cliente@correo.com"}'

4 · Gestionar (promotor)

Todo el autoservicio del promotor pasa por una función: reseller-api. Siempre es POST con body { "action": "…", … } y autenticación por x-api-key o Authorization: Bearer. Abajo, cada acción con su body y respuesta.

POST /reseller-api x-api-key o Bearer

Ejemplo genérico

curl -X POST https://eosxwcbciazxontwytie.supabase.co/functions/v1/reseller-api \
  -H "x-api-key: rk_live_xxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{"action":"summary"}'

summary — resumen del panel

Body: solo action. Devuelve datos del promotor, cupo, piso general y últimas ventas.

{
  "ok": true,
  "reseller": { "id":"…", "email":"…", "name":"…", "delivery_mode":"auto_email",
                "stripe_configured": true, "test_mode": false, "api_key_prefix":"rk_live_abc",
                "branding": {…}, "prices": {…} },
  "quota": { "total": 100, "used": 12, "available": 88 },
  "floor": { "amount": 150000, "currency": "MXN" },
  "sales_count": 12,
  "last_sales": [ { "id":"…", "kind":"sale", "buyer_email":"…", "amount":350000, "currency":"MXN", "licenses": { "key":"LO-…" } } ]
}

sales — detalle de ventas

Body: { action, limit? } (limit 1–200, def. 50). Devuelve { ok, sales:[…] } con clave, estado y update_until de cada licencia.

metrics — funnel, ventas y campañas

Body: { action, from?, to? } (ISO; def. últimos 30 días).

{
  "ok": true, "from": "…", "to": "…",
  "funnel": { "view": 800, "click": 300, "begin_checkout": 120, "purchase": 40 },
  "conversion_rate": 5.0,
  "sales": 40, "revenue": 14000000,
  "by_campaign": [ { "campaign":"…", "views":…, "clicks":…, "sales":…, "revenue":… } ],
  "series": [ { "day":"2026-07-01", "views":…, "purchases":… } ]
}

set-prices — fijar precios por producto

Body: { action, prices } donde prices es un objeto por slug: { amount, currency } (≥ piso, moneda exacta), o { hidden: true } para ocultarlo, o sin amount para volver a precio de lista. Devuelve { ok, prices }.

{"action":"set-prices","prices":{"leadops":{"amount":390000,"currency":"MXN"}}}

set-branding — marca de la tienda

Body: { action, branding } (se hace merge). Campos: name, logo, accent. Devuelve { ok, branding }.

{"action":"set-branding","branding":{"name":"Acme Software","accent":"#0aa","logo":"https://…/logo.png"}}

set-delivery — modo de entrega

Body: { action, mode } con mode = "auto_email" o "self". Devuelve { ok, delivery_mode }.

set-marketing — pixeles / conversiones

Body: { action, marketing } con IDs públicos (meta_pixel_id, ga4_measurement_id, tiktok_pixel_id) y tokens secretos (meta_capi_token, ga4_api_secret, se cifran; vacío = borrar). La respuesta nunca devuelve los tokens, solo flags meta_capi / ga4_secret.

batch-quote — cotizar cupo (sin efecto)

Body: { action, qty }. Devuelve el precio por volumen antes de comprar.

{ "ok": true, "min": 50, "qty": 100, "valid": true, "unit": 75000, "total": 7500000, "currency": "MXN", "tiers": [ { "min":50, "amount":80000 }, … ] }

buy-batch — comprar cupo

Body: { action, qty, test? } (qty 50–500). Crea un Checkout en el Stripe de la matriz; el cupo se acredita solo al pagar. Devuelve { url }.

stripe-setup — conectar tu Stripe

Body: { action, sk, pk?, test? }. Valida tu llave secreta, crea el webhook en tu cuenta (apuntando a reseller-mint) y cifra sk + whsec. Devuelve { ok, account_id, test_mode }.

{"action":"stripe-setup","sk":"sk_live_xxx","pk":"pk_live_xxx"}

stripe-status — salud de la conexión

Body: solo action. Devuelve { ok, configured, endpoint_status?, account_id }.

domain-add / domain-status / domain-remove — dominios propios

domain-add: { action, hostname, target? } (target def. store) → alta de Custom Hostname (Cloudflare for SaaS); si el alta automática no está habilitada devuelve { manual:true, instructions, cname_target }. domain-status: { action, id | hostname } → estado de DNS/SSL. domain-remove: { action, id }{ ok, removed:true }. (domains lista todos.)

rotate-api-key — nueva API key

Body: solo action. Genera una nueva rk_live_… (se muestra una vez) e invalida la anterior. Devuelve { ok, api_key, api_key_prefix }.

5 · Licencias (para apps)

Endpoints que consumen las apps de escritorio construidas sobre la plataforma: activan una licencia en una máquina y refrescan su estado. Devuelven un token firmado (Ed25519) que la app verifica offline. No requieren credenciales de promotor.

POST /activate Sin auth

Valida la clave (o inicia una prueba), aplica el límite de asientos por máquina y devuelve el token firmado. La licencia de promotor se ata a su producto en la primera activación.

Body

CampoTipoDescripción
machine_idstringobligatorioIdentificador estable de la máquina.
keystringcondicionalClave de licencia. Obligatoria salvo que se inicie una prueba.
productSlugstringopcionalProducto a activar (def. leadops).
trialbooleanopcionalCon true y sin key, inicia/recupera la prueba (14 días) anclada a la máquina.
machine_name, os, app_versionstringopcionalMetadatos de la activación.

Respuesta

{ "token": "<payloadB64>.<sigB64>" }

Ejemplo

curl -X POST https://eosxwcbciazxontwytie.supabase.co/functions/v1/activate \
  -H "Content-Type: application/json" \
  -d '{"productSlug":"leadops","key":"LO-XXXX-XXXX-XXXX-XXXX","machine_id":"m-123","os":"win"}'
POST /validate Sin auth

Heartbeat: refresca el token con el estado actual de la licencia y actualiza last_seen. Si la licencia o la activación ya no valen, devuelve { revoked: true } para que la app se bloquee.

Body

CampoTipoDescripción
machine_idstringobligatorioLa misma máquina de la activación.
keystringcondicionalClave, o bien manda token (se lee la clave de su payload).
tokenstringcondicionalToken actual; alternativa a key.

Respuesta

{ "token": "<nuevo token firmado>" }   // o  { "revoked": true }

Ejemplo

curl -X POST https://eosxwcbciazxontwytie.supabase.co/functions/v1/validate \
  -H "Content-Type: application/json" \
  -d '{"key":"LO-XXXX-XXXX-XXXX-XXXX","machine_id":"m-123"}'

6 · Analítica

Ingesta pública de eventos de funnel que emiten el storefront y las landings. Fire-and-forget: siempre responde 200 y nunca bloquea la página. Anti-PII: solo se guardan campos conocidos.

POST /track Sin auth

Registra eventos del embudo. Acepta un lote { events:[…] } (máx. 50) o un evento suelto. Los eventos inválidos y las claves desconocidas se descartan.

Campos de cada evento

CampoTipoDescripción
eventstringobligatorioUno de view, click, begin_checkout, purchase.
product_slugstringopcionalSlug del producto.
session_idstringopcionalID de sesión anónima del navegador.
seller_kindstringopcional"reseller" para atribuir al promotor (requiere reseller_id).
reseller_iduuidopcionalID del promotor (uuid válido); si falta, la venta es de la matriz.
utmobjectopcionalSolo claves utm_*.
hostnamestringopcionalHost de la página.

Respuesta

{ "ok": true, "accepted": 2 }

Ejemplo

curl -X POST https://eosxwcbciazxontwytie.supabase.co/functions/v1/track \
  -H "Content-Type: application/json" \
  -d '{"events":[{"event":"view","product_slug":"leadops","seller_kind":"reseller","reseller_id":"RESELLER_ID"}]}'