Referencia de API

La API de CustomerHero te permite gestionar chatbots, clientes, conversaciones y más de forma programática. Todos los endpoints devuelven respuestas en JSON.

Autenticación

La mayoría de los endpoints de la API requieren autenticación mediante una clave de API. Incluye tu clave en la cabecera Authorization como token Bearer.

Crea claves de API en la configuración de tu panel.

curl https://api.customerhero.app/api/chatbots \
  -H "Authorization: Bearer sk_live_tu_clave_api"

URL base

CustomerHero ofrece hosts regionales para que tus datos permanezcan en la región que elegiste al crear tu organización. Envía las solicitudes al host que coincide con la región de tu organización:

# EE. UU. (predeterminado)
https://us.api.customerhero.app

# UE (residencia de datos GDPR)
https://eu.api.customerhero.app

El host heredado https://api.customerhero.app también acepta solicitudes y las redirige a la región correcta; pero usar el host regional directamente evita una redirección en cada solicitud.

La especificación OpenAPI completa está disponible en /api/openapi.json (se abre en una pestaña nueva).

Errores

La API usa códigos de estado HTTP estándar. Las respuestas de error incluyen un cuerpo JSON con un campo error.

EstadoSignificado
200Éxito
201Creado
400Solicitud incorrecta — parámetros faltantes o no válidos
401No autorizado — clave de API no válida o ausente
403Prohibido — permisos insuficientes
402Pago requerido — se alcanzó el límite del plan o se acabaron los créditos
404No encontrado — el recurso no existe
429Demasiadas solicitudes — se alcanzó el límite de tasa
500Error interno del servidor
{
  "error": "Unauthorized"
}

Límites de tasa e idempotencia

La API protege la infraestructura compartida con límites de tasa por clave, por ruta y por IP. Cuando superas un límite recibes una respuesta HTTP 429 con una cabecera Retry-After (en segundos). Espera al menos ese tiempo antes de reintentar. Las rutas calientes como el endpoint público de chat y los flujos de login tienen límites más estrictos que el CRUD del panel.

Estrategia de reintento. Trata 429 y 5xx como transitorios. Reintenta con retroceso exponencial y jitter (p. ej. 1 s, 2 s, 4 s, 8 s con ±20 % de jitter). Limita a 5 intentos. Nunca reintentes con 4xx distintos de 429: son errores del cliente.

Idempotencia. Las solicitudes mutantes (POST, PUT, PATCH) aceptan la cabecera Idempotency-Key. Reutilizar la misma clave dentro de 24 horas devuelve la respuesta original en lugar de ejecutar la operación de nuevo. Usa un UUID nuevo por operación lógica:

curl https://us.api.customerhero.app/api/orgs/ORG_ID/customers \
  -X POST \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: 8f4b2c0e-3a91-4d2f-9a4f-0d3a5b1e8c77" \
  -H "Content-Type: application/json" \
  -d '{"email":"jane@acme.com"}'

Webhooks

Suscríbete a eventos desde la configuración de webhooks del panel. Cada suscripción recibe un POST por evento, firmado con un secreto HMAC-SHA256 que defines al crearla. Las solicitudes incluyen una cabecera Idempotency-Key igual al id del evento, para que los receptores puedan deduplicar reintentos con seguridad.

Cabeceras

CabeceraValor
X-CustomerHero-Event-IdIdentificador estable de esta entrega — úsalo como tu clave de idempotencia.
X-CustomerHero-Event-TypeNombre del evento, p. ej. conversation.created.
X-CustomerHero-TimestampMarca de tiempo Unix (segundos) en la que se firmó la solicitud.
X-CustomerHero-Signaturet=<timestamp>,v1=<hmac-sha256-hex>. v1 es el único esquema actual; los nuevos se añadirán sin retirarlo.
Idempotency-KeyRefleja X-CustomerHero-Event-Id para que los clientes HTTP genéricos puedan deduplicar.

Esquema de firma

La firma es hmac_sha256(secret, "<timestamp>.<cuerpo_crudo>"), codificada en hex. Usa el cuerpo crudo de la solicitud — re-serializar el JSON cambia los bytes y rompe la verificación. Rechaza solicitudes con un timestamp con más de 5 minutos de desfase respecto al reloj de tu servidor para evitar ataques de repetición.

Ejemplos de verificación

Cada fragmento parsea la cabecera de firma, recalcula el HMAC sobre t.body y compara en tiempo constante.

Node.js

const crypto = require('crypto');

function verify(rawBody, headers, secret) {
  const sigHeader = headers['x-customerhero-signature'] || '';
  const parts = Object.fromEntries(
    sigHeader.split(',').map((p) => p.split('=')),
  );
  const ts = parts.t;
  const v1 = parts.v1;
  if (!ts || !v1) return false;

  if (Math.abs(Date.now() / 1000 - Number(ts)) > 300) return false;

  const expected = crypto
    .createHmac('sha256', secret)
    .update(`${ts}.${rawBody}`)
    .digest('hex');

  const a = Buffer.from(v1, 'hex');
  const b = Buffer.from(expected, 'hex');
  return a.length === b.length && crypto.timingSafeEqual(a, b);
}

Python

import hmac, hashlib, time

def verify(raw_body: bytes, headers: dict, secret: str) -> bool:
    sig_header = headers.get("X-CustomerHero-Signature", "")
    parts = dict(p.split("=", 1) for p in sig_header.split(",") if "=" in p)
    ts = parts.get("t")
    v1 = parts.get("v1")
    if not ts or not v1:
        return False

    if abs(time.time() - int(ts)) > 300:
        return False

    expected = hmac.new(
        secret.encode(),
        f"{ts}.".encode() + raw_body,
        hashlib.sha256,
    ).hexdigest()

    return hmac.compare_digest(v1, expected)

Ruby

require 'openssl'

def verify(raw_body, headers, secret)
  sig_header = headers['X-CustomerHero-Signature'].to_s
  parts = sig_header.split(',').to_h { |p| p.split('=', 2) }
  ts = parts['t']
  v1 = parts['v1']
  return false unless ts && v1
  return false if (Time.now.to_i - ts.to_i).abs > 300

  expected = OpenSSL::HMAC.hexdigest('SHA256', secret, "#{ts}.#{raw_body}")
  Rack::Utils.secure_compare(v1, expected)
end

PHP

<?php
function verify(string $rawBody, array $headers, string $secret): bool {
    $sigHeader = $headers['X-CustomerHero-Signature'] ?? '';
    $parts = [];
    foreach (explode(',', $sigHeader) as $pair) {
        [$k, $v] = array_pad(explode('=', $pair, 2), 2, null);
        if ($k && $v) $parts[$k] = $v;
    }
    $ts = $parts['t'] ?? null;
    $v1 = $parts['v1'] ?? null;
    if (!$ts || !$v1) return false;

    if (abs(time() - (int)$ts) > 300) return false;

    $expected = hash_hmac('sha256', $ts . '.' . $rawBody, $secret);
    return hash_equals($expected, $v1);
}

Incrustación del widget

Añade el widget de chat a tu sitio web con un único script. Consulta la guía del Widget para ver todas las opciones de personalización.

<script
  src="https://widget.customerhero.app/v1/loader.js"
  data-chatbot-id="TU_CHATBOT_ID"
  data-api-base="https://us.api.customerhero.app"
  async
></script>

Chatbots

Manage your chatbots — create, list, update, and delete.

Conversations

List, view, and update conversations. Manage tags and timeline.

Customers

Manage customer profiles — list, create, update, merge, and delete.

Companies

Manage company profiles and their contacts.

Data Sources

Add and manage training data for chatbots — URLs, text, Q&A, and files.

Channels

Configure messaging channels — widget, WhatsApp, email, and more.

Tags

Manage conversation tags for your organization.

Canned Responses

Manage reusable response templates.

API Keys

Manage API keys for programmatic access.

Team

Manage team members and invitations.

Avatar

Test customer data endpoint

Analytics

Conversation statuses

Customer properties

Macros

Macro runs

Agents

Inbox teams

Billing

Saved searches

Actions

Workflows

Triggers

Pre chat form

Notification settings

Stale conversation notifications

Routing rules

Reports

Analytics targets

Mentions

Dsr

Privacy

Retention

Sla policies

Webhook subscriptions

Webhook event types

Audit log

Abuse

Sso connections

Público

Network

Scim tokens

Calendar

Integrations

Help sites

Identity secrets

Identity stats

Verified domains