API HTTP de Postari

REST API para integraciones B2B · auth Bearer · respuesta JSON · webhooks HMAC-firmados para eventos email_delivered / email_opened / email_clicked / email_bounced / email_complained / email_unsubscribed.

v1 · en producción · feedback bienvenido

1 · Crea tu API key

Ve a /ajustes/seguridad y click "Crear API key". Elige el scope:

  • read · solo lectura · subscribers, stats, campañas
  • write · agregar/editar subscribers, tags, metadata
  • send · disparar envíos de campañas
  • admin · todo lo anterior + dominios + DELETE

La key se muestra UNA SOLA VEZ. Guárdala en tu vault. Formato: ef_live_xxxx

2 · Autenticación

Pasa la key como Bearer token en el header Authorization:

curl https://postari.io/api/v1/stats \
  -H "Authorization: Bearer ef_live_xxxxxxxxxxxxxxxx"

Alternativa: header x-api-key · mismo valor sin "Bearer ".

3 · Enviar campaña

POST /api/v1/campaigns/send · scope send

curl -X POST https://postari.io/api/v1/campaigns/send \
  -H "Authorization: Bearer ef_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "list_id": "uuid-de-tu-lista",
    "subject": "🚀 Nuevo curso disponible",
    "from_name": "Mara López",
    "from_email": "mara@coachmara.com",
    "html_body": "<h1>Hola {{first_name}}</h1><p>Tu curso ya está...</p>",
    "send_to": {
      "include_tags": ["tier:premium"],
      "exclude_tags": ["unsubscribed_promos"]
    },
    "scheduled_at": "2026-05-12T15:00:00Z"
  }'

Variables soportadas en el HTML: {{first_name}} {{from_name}} {{tenant_name}} {{unsubscribe_url}}

4 · Subscribers (CRUD)

# Agregar 1 subscriber
curl -X POST https://postari.io/api/v1/subscribers \
  -H "Authorization: Bearer ef_live_xxx" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "miembro@gmail.com",
    "name": "Juan Pérez",
    "tags": ["tier:premium", "joined:2026-04"],
    "metadata": { "faroli_id": "cm456def", "ltv_cents": 29700 }
  }'

# Bulk import (max 5000 por request)
curl -X POST https://postari.io/api/v1/subscribers/bulk \
  -H "Authorization: Bearer ef_live_xxx" \
  -d '{
    "subscribers": [
      { "email": "uno@example.com", "tags": ["nuevo"] },
      { "email": "dos@example.com", "tags": ["nuevo"] }
    ],
    "on_conflict": "ignore"
  }'

# Listar paginado con cursor
curl "https://postari.io/api/v1/subscribers?limit=100&tag=tier:premium" \
  -H "Authorization: Bearer ef_live_xxx"

# Update tags
curl -X PUT https://postari.io/api/v1/subscribers/miembro%40gmail.com \
  -H "Authorization: Bearer ef_live_xxx" \
  -d '{ "tags": ["tier:premium", "completed:lesson3"] }'

# Soft delete
curl -X DELETE https://postari.io/api/v1/subscribers/miembro%40gmail.com \
  -H "Authorization: Bearer ef_live_xxx"

5 · Dominio + DKIM

Cada cuenta puede configurar 1 dominio sender. Postari devuelve los DNS records (DKIM CNAMEs + SPF + DMARC) que el creator pega en su DNS provider.

# Registrar dominio
curl -X POST https://postari.io/api/v1/domains \
  -H "Authorization: Bearer ef_live_xxx" \
  -d '{
    "domain": "coachmara.com",
    "from_email": "mara@coachmara.com",
    "from_name": "Mara López"
  }'

# Response incluye dns_records con CNAMEs DKIM, SPF y DMARC
# El creator pega esos records en Cloudflare/Hostinger/etc.

# Verificar (después de propagación DNS · típicamente 30 min - 24h)
curl -X POST "https://postari.io/api/v1/domains/coachmara.com?action=verify" \
  -H "Authorization: Bearer ef_live_xxx"

# Estado actual
curl https://postari.io/api/v1/domains/coachmara.com \
  -H "Authorization: Bearer ef_live_xxx"

6 · Webhooks de eventos

Configura un webhook en /ajustes/webhooks. Postari envía POST con HMAC-SHA256 signature en header X-Postari-Signature.

Eventos disponibles:

  • email_delivered · Resend confirma entrega
  • email_opened · pixel disparado
  • email_clicked · click en link
  • email_bounced · rebotó (hard/soft)
  • email_complained · marcó como spam
  • email_unsubscribed · usó link unsub
# Payload típico
{
  "event": "email_opened",
  "tenant_id": "uuid",
  "fired_at": "2026-05-09T20:30:00Z",
  "payload": {
    "email_id": "uuid",
    "contact_id": "uuid",
    "campaign_id": "uuid",
    "subject": "Tu asunto",
    "device_type": "mobile",
    "email_client": "apple_mail",
    "opened_at": "2026-05-09T20:30:00Z"
  }
}

# Verificar signature en tu servidor (Node.js)
const expected = crypto
  .createHmac('sha256', WEBHOOK_SECRET)
  .update(rawBody)
  .digest('hex');
if (expected !== req.headers['x-postari-signature']) {
  return res.status(401).end();
}

7 · Stats y analytics

# Resumen del período
curl "https://postari.io/api/v1/stats?from=2026-04-01&to=2026-05-01" \
  -H "Authorization: Bearer ef_live_xxx"

# Stats por campaña (incluye by_link[])
curl https://postari.io/api/v1/campaigns/uuid-campaña/stats \
  -H "Authorization: Bearer ef_live_xxx"

by_link[]: cada URL clickeada con clicks totales y unique_clicks (1 por contact).

8 · Códigos de error

CódigoCuándo
400Body inválido o campos requeridos faltantes
401API key inválida, expirada o revocada
403API key sin scope suficiente para este endpoint
404Recurso (subscriber, campaña, dominio) no encontrado
409Conflicto · subscriber ya existe, dominio ya configurado
422Validation error · ej. dominio no verificado al intentar enviar
423Cuenta con envíos congelados (sending_frozen)
429Rate limit excedido · header Retry-After indica cuándo reintentar
502Error con provider externo (Resend, etc.)
500Error interno · contacta soporte

9 · Platform mode (multi-tenant)

Si Postari es la infraestructura email que ofreces a tus clientes (estilo Stripe Connect / SendGrid sub-accounts), activa platform mode en /ajustes/seguridad/platform. Una vez activo, tu API key con scope admin + platform_capable puede:

  • · Crear sub-cuentas vía POST /api/v1/accounts
  • · Operar sobre cualquier sub-cuenta pasando header X-Postari-Account-Id: <sub-uuid>
  • · Ver stats agregados por sub-cuenta
  • · Cerrar sub-cuentas (soft delete)
# Crear sub-cuenta (uno de tus clientes)
curl -X POST https://postari.io/api/v1/accounts \
  -H "Authorization: Bearer ef_live_PLATFORM_KEY" \
  -d '{
    "external_id": "your_internal_id_for_this_client",
    "name": "Coach Mara López",
    "owner_email": "mara@coachmara.com",
    "metadata": { "your_plan": "PRO", "members": 850 }
  }'

# Response incluye api_key auto-generada para esa sub-cuenta
# (la puedes guardar o ignorarla y usar tu PLATFORM_KEY + X-Postari-Account-Id)

# Listar todas tus sub-cuentas (paginado)
curl https://postari.io/api/v1/accounts \
  -H "Authorization: Bearer ef_live_PLATFORM_KEY"

# Operar sobre una sub-cuenta específica · TODOS los endpoints v1
# aceptan el header X-Postari-Account-Id
curl -X POST https://postari.io/api/v1/campaigns/send \
  -H "Authorization: Bearer ef_live_PLATFORM_KEY" \
  -H "X-Postari-Account-Id: <sub-tenant-uuid>" \
  -d '{ "list_id": "...", "subject": "...", "html_body": "..." }'

# Bulk import subscribers a una sub-cuenta
curl -X POST https://postari.io/api/v1/subscribers/bulk \
  -H "Authorization: Bearer ef_live_PLATFORM_KEY" \
  -H "X-Postari-Account-Id: <sub-tenant-uuid>" \
  -d '{ "subscribers": [...] }'

# Cerrar sub-cuenta (soft · congela envíos + revoca API keys)
curl -X DELETE https://postari.io/api/v1/accounts/<sub-tenant-uuid> \
  -H "Authorization: Bearer ef_live_PLATFORM_KEY"

Aislamiento por Postgres RLS: cada sub-cuenta tiene su propio dominio DKIM, sus contactos, sus campañas y sus stats · separadas por tenant_id en cada tabla con row-level security activo + verificación explícita en cada endpoint.

10 · Rate limits

  • · 100 req/min por API key (subible para clientes enterprise)
  • · 5000 subscribers max por request bulk (chunkear si tienes más)
  • · Send recipients según plan del tenant: 25K (Creator) · 100K (Pro) · 300K (Business) · ilimitado (Enterprise)
  • · Webhook timeout: 10 segundos · si tu endpoint no responde, reintentamos 3 veces con exponential backoff

¿Dudas o reportar bug? Email hi@postari.io

Postari API v1 · base URL https://postari.io/api/v1