API Reference

The CustomerHero API lets you programmatically manage chatbots, customers, conversations, and more. All endpoints return JSON responses.

Authentication

Most API endpoints require authentication via an API key. Include your key in the Authorization header as a Bearer token.

Create API keys in your dashboard settings.

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

Base URL

CustomerHero serves regional API hosts so your data stays in the region you picked when you created your organization. Send requests to the host that matches your org's region:

# US (default)
https://us.api.customerhero.app

# EU (GDPR data residency)
https://eu.api.customerhero.app

The legacy host https://api.customerhero.app also accepts requests and routes them to the right region — but picking the regional host directly avoids a redirect on every request.

The full OpenAPI specification is available at /api/openapi.json (opens in new tab).

Errors

The API uses standard HTTP status codes. Error responses include a JSON body with an error field.

StatusMeaning
200Success
201Created
400Bad request — missing or invalid parameters
401Unauthorized — invalid or missing API key
403Forbidden — insufficient permissions
402Payment required — plan limit hit or out of credits
404Not found — resource does not exist
429Too many requests — rate limit hit
500Internal server error
{
  "error": "Unauthorized"
}

Rate limits & idempotency

The API protects shared infrastructure with per-key, per-route, and per-IP rate limits. When you exceed a limit you get an HTTP 429 response with a Retry-After header (seconds). Wait at least that long before retrying. Hot paths like the public chat endpoint and login flows have stricter limits than dashboard CRUD.

Retry strategy. Treat 429 and 5xx as transient. Retry with exponential backoff and jitter (e.g. 1s, 2s, 4s, 8s with ±20% jitter). Cap at 5 attempts. Never retry on 4xx other than 429 — those are caller errors.

Idempotency. Mutating requests (POST, PUT, PATCH) accept an Idempotency-Key header. Reusing the same key within 24 hours returns the original response instead of executing the operation a second time. Use a fresh UUID per logical operation:

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

Subscribe to events from your dashboard webhook settings. Each subscription receives a POST per event, signed with an HMAC-SHA256 secret you set at subscription time. Requests carry an Idempotency-Key equal to the event id, so safe receivers can dedupe retries.

Headers

HeaderValue
X-CustomerHero-Event-IdStable id for this delivery — use it as your idempotency key.
X-CustomerHero-Event-TypeEvent name, e.g. conversation.created.
X-CustomerHero-TimestampUnix timestamp (seconds) when the request was signed.
X-CustomerHero-Signaturet=<timestamp>,v1=<hex-hmac-sha256>. v1 is the only scheme today; new schemes will be added without removing v1.
Idempotency-KeyMirrors X-CustomerHero-Event-Id so generic HTTP clients can dedupe.

Signature scheme

The signature is hmac_sha256(secret, "<timestamp>.<raw_body>"), hex-encoded. Use the raw request body — re-serializing the JSON will change the bytes and break verification. Reject requests with a timestamp more than 5 minutes off your server clock to prevent replay attacks.

Verification examples

Each snippet parses the signature header, recomputes the HMAC over t.body, and compares in constant time.

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);
}

Widget Embed

Add the chat widget to your website with a single script tag. See the Widget guide for full customization options.

<script
  src="https://widget.customerhero.app/v1/loader.js"
  data-chatbot-id="YOUR_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

Public

Network

Scim tokens

Calendar

Integrations

Help sites

Identity secrets

Identity stats

Verified domains