LealUp Docs
API y Webhooks

Autenticación

OAuth2 (OIDC) para apps, API keys para integraciones server-to-server, bearer tokens.

LealUp soporta dos mecanismos de autenticación:

  • OAuth2 (OIDC) — para apps que actúan en nombre de un usuario.
  • API keys — para integraciones server-to-server (scripts, backends, sync jobs).

Ambos emiten bearer tokens que se envían en el header Authorization.

OAuth2 / OIDC

Usa este flow cuando tu integración actúa en nombre de un usuario específico (ej. una app que muestra datos de LealUp dentro de tu producto).

Setup (una vez)

  1. Admin → API → OAuth Apps → Crear app.
  2. Configura:
    • Name — visible a usuarios durante el consent.
    • Redirect URIs — uno o más (ej. https://tuapp.com/callback).
    • Scopes solicitados — ver lista abajo.
  3. Recibes:
    • client_id — público.
    • client_secretmantén secreto (almacena en vault, nunca en código cliente).

Flow (Authorization Code with PKCE — obligatorio)

1. Redirect user a:
   https://api.lealup.com/v1/oauth/authorize?
     response_type=code&
     client_id=YOUR_CLIENT_ID&
     redirect_uri=https://tuapp.com/callback&
     scope=customers:read playbooks:read&
     state=RANDOM_NONCE&
     code_challenge=CODE_CHALLENGE&
     code_challenge_method=S256

2. Usuario da consent → redirect a tu callback con ?code=AUTH_CODE

3. Intercambia code por token:
   POST https://api.lealup.com/v1/oauth/token
   Content-Type: application/x-www-form-urlencoded

   grant_type=authorization_code&
   code=AUTH_CODE&
   redirect_uri=https://tuapp.com/callback&
   client_id=YOUR_CLIENT_ID&
   client_secret=YOUR_CLIENT_SECRET&
   code_verifier=CODE_VERIFIER

4. Respuesta:
   {
     "access_token": "eyJ...",
     "refresh_token": "eyJ...",
     "token_type": "Bearer",
     "expires_in": 3600,
     "scope": "customers:read playbooks:read",
     "tenant_id": "01HXYZ..."
   }

Usar el token

curl https://api.lealup.com/v1/customers \
  -H "Authorization: Bearer eyJ..."

Refresh

Los access tokens duran 1h. El refresh token dura 90 días y rota en cada uso.

POST /v1/oauth/token
grant_type=refresh_token&
refresh_token=OLD_REFRESH_TOKEN&
client_id=YOUR_CLIENT_ID&
client_secret=YOUR_CLIENT_SECRET

Respuesta incluye nuevo access_token + nuevo refresh_token (el anterior se invalida).

API keys

Usa API keys para integraciones server-to-server sin usuario humano (cron jobs, sync scripts, webhooks entrantes).

Crear

Admin → API → API Keys → Nueva key

Campos:

  • Name — identificador legible ("data warehouse sync", "alertas zendesk").
  • Scopes — qué puede hacer la key (ver lista).
  • IP allowlist — opcional, CIDR ranges.
  • Expires — opcional (recomendado 90 días).

Obtienes:

lealup_sk_live_abc123def456...

La key se muestra una sola vez. Copiala y almacénala en vault/secrets manager.

Formato

  • lealup_sk_live_* — production.
  • lealup_sk_test_* — dev.

Usar

Header directo:

curl https://api.lealup.com/v1/customers \
  -H "Authorization: Bearer lealup_sk_live_abc123def456..."

Rotación

  • Crea una key nueva.
  • Deploy con la nueva key.
  • Revoca la vieja en Admin → API → API Keys → [key] → Revocar.

Buena práctica: rotar cada 90 días, después de cualquier suspicion de leak, o cuando alguien con acceso sale de la empresa.

Revocar

Inmediato — Admin → API → API Keys → [key] → Revocar. Requests con la key revocada devuelven 401.

Scopes

Los scopes controlan qué puede hacer un token.

Formato

{recurso}:{acción}, ej. customers:read, events:write.

Lista completa

ScopePermite
customers:readGET customers y contactos
customers:writePOST/PATCH customers y contactos
customers:deleteDELETE customers (soft-delete)
health:readGET health scores
health:writePOST recalculate, PATCH modelos
playbooks:readGET playbooks y tareas
playbooks:writePOST/PATCH playbooks
tasks:readGET tasks
tasks:writePATCH tasks, complete tasks
events:writePOST events (ingesta)
events:readGET events (debugging)
webhooks:readGET webhook subscriptions
webhooks:writePOST/PATCH webhook subscriptions
users:readGET users
users:writePOST invite, PATCH role (restringido)
analytics:readGET analytics endpoints
admin:*acceso completo admin (solo OAuth con rol admin o key creada por admin)

Pide el mínimo necesario. Si solo vas a leer, no pidas :write.

Errores de auth

StatusCódigoSignificado
401unauthenticatedno se envió token, o está malformado
401token_expiredaccess token expiró — usa refresh
401token_revokedkey o token fue revocado
403insufficient_scopeel token es válido pero no tiene el scope necesario
403ip_not_allowedkey tiene allowlist y tu IP no está
429rate_limitedexceso de requests — ver header Retry-After

Ejemplo de respuesta:

{
  "type": "https://api.lealup.com/errors/insufficient_scope",
  "title": "Insufficient scope",
  "status": 403,
  "detail": "Required scope 'customers:write' but token only has 'customers:read'",
  "instance": "/v1/customers",
  "trace_id": "01HXYZ123ABC"
}

Incluye siempre el trace_id en issues reportados a soporte.

Seguridad

  • HTTPS only — requests a HTTP redireccionan a HTTPS; algunos endpoints rechazan con 400.
  • Token expiration — access tokens son cortos (1h). Refresh tokens largos (90 días) pero rotan.
  • API keys en reposo: almacenadas hasheadas (SHA-256 + salt). Nosotros no podemos recuperarlas.
  • Audit log — todo uso de API keys queda en el audit log con actor_id, trace_id, endpoint, timestamp.
  • Anomaly detection — picos de uso inusuales disparan alerta al admin.

Identidad vs autorización

  • Autenticación (quién) — el token válido.
  • Autorización (qué puede hacer) — scopes + rol del usuario/key.

Un token puede ser válido (auth OK) pero no tener el scope para una acción → 403, no 401.

Ejemplo end-to-end (Python)

import httpx

BASE = "https://api.lealup.com"
KEY = "lealup_sk_live_..."

async def list_customers():
    async with httpx.AsyncClient() as client:
        r = await client.get(
            f"{BASE}/v1/customers",
            headers={"Authorization": f"Bearer {KEY}"},
            params={"limit": 50, "status": "active"},
        )
        r.raise_for_status()
        return r.json()

Ejemplo end-to-end (TypeScript con SDK)

import { LealUpClient } from "@lealup/sdk";

const client = new LealUpClient({
  apiKey: process.env.LEALUP_API_KEY!,
});

const customers = await client.customers.list({
  status: "active",
  limit: 50,
});

On this page