Webhooks
Customer Created Webhook

customer.created webhook

The customer.created event is delivered whenever one or more new customer records are created in your Customerscore.io account — whether you create them through the REST API, upload a CSV, or an integration sync (HubSpot, Salesforce, Stripe, …) brings in customers that did not exist before. Use it to mirror new accounts into your own systems, kick off onboarding automations, or keep an external ID mapping in sync.

Heads-up on batching: a single import can create thousands of customers at once. Customerscore.io groups newly created customers into batches of up to 1000 and sends one customer.created request per batch. Always read the batch object (below) to know whether more requests are coming.

Heads-up on timing: the event fires at the moment the record is inserted, before any scoring or attribute processing runs. The payload therefore carries only identity fields — customerscore_id, external_id, name, created_at — and the import source. Health score, churn risk, owner, tags and custom properties are not available yet. Fetch them later with Get Customer when you need them.


When it's triggered

A customer.created request is dispatched when all of the following are true:

  1. Your account has at least one active webhook subscribed to the customer.created event.
  2. A customer record (not a contact) with an external_id that did not previously exist in your account is inserted.

Only genuinely new records fire the event. Re-importing or re-syncing a customer whose external_id already exists updates it silently and produces no webhook.

Sourcedata.sourceDescription
Create Customer (POST /v1/customer)APIOne customer per request; the webhook is dispatched immediately after the record is created.
Customer upload (POST /v1/customers)APIBulk upload. New external_ids are created while the upload is processed in the background. Only when the API is your account's source of truth.
CSV import from the appFILEOnly when file import is your account's source of truth.
Stripe integrationSTRIPENew Stripe customers found during a sync. Only when Stripe is your account's source of truth.
CRM / data-source integrations (HubSpot, Salesforce, Pipedrive, Raynet, PostHog, Mixpanel, …)HUBSPOT, SALESFORCE, PIPEDRIVE, RAYNET, POSTHOG, …New records found during a scheduled sync of the integration configured as your account's source of truth. The value is the integration's identifier in UPPERCASE.

Contacts (people belonging to a customer) never fire this event.


Delivery

PropertyValue
HTTP methodPOST
Content-Typeapplication/json
User-AgentCustomerscore.io-Webhook/1.0
Signature headerX-customerscore-HMAC: sha256=<hex> (see Verifying the signature)
Timeout10 seconds
RetriesUp to 3 attempts, exponential backoff (first retry after ~5s)

Response handling:

  • 2xx — treated as a successful delivery; no retry.
  • 5xx — treated as a transient failure and retried (up to the retry limit).
  • 4xx — treated as a permanent failure; not retried. Make sure your endpoint returns a 2xx once it has accepted the payload.

Your endpoint should respond quickly (within the 10s timeout). If you need to do heavy processing, acknowledge with a 2xx immediately and process the payload asynchronously.

Because failed deliveries are retried, your endpoint may occasionally receive the same batch twice. Treat customerscore_id as the idempotency key.


Payload schema

Every webhook is wrapped in a common envelope:

{
  "event": "customer.created",
  "timestamp": "2026-09-03T10:00:00.000Z",
  "data": { ... }
}
FieldTypeDescription
eventstringAlways "customer.created" for this event.
timestampstring (ISO-8601)When the webhook payload was generated (UTC).
dataobjectThe event payload — see below.

data

FieldTypeNullableDescription
sourcestringnoWhich import created the customers — see data.source.
customersarraynoThe newly created customers in this batch — see data.customers.
batchobjectnoBatching metadata — see data.batch.

data.source

An UPPERCASE identifier of the data source that created the customers. Common values:

ValueMeaning
APIREST API — single create or bulk upload.
FILECSV import from the app.
STRIPEStripe integration sync.
HUBSPOT, SALESFORCE, PIPEDRIVE, RAYNET, …The corresponding CRM / data-source integration sync.

New integrations may introduce new values over time, so treat this field as an open set.

data.customers

Each entry represents one newly created customer.

FieldTypeNullableDescription
customerscore_idnumbernoCustomerscore.io's internal ID of the record. Identical to customerscore_id returned by Get Customer.
external_idstringnoYour identifier for the customer (the ID you sync to Customerscore.io). Use it with GET /v1/customer/{external_id}.
namestringyesThe customer's name as known at creation time. May be null or empty if the source did not provide one.
created_atstring (ISO-8601)noWhen the record was inserted (UTC).

data.batch

FieldTypeDescription
current_batchnumber1-based index of this batch.
total_batchesnumberTotal number of batches in this group of created customers.
total_customersnumberTotal customers created in this group, across all batches.
batch_sizenumberNumber of customers in this batch (≤ 1000).

Note: bulk imports are processed in chunks. A large import (for example an initial CRM sync of 5,000 customers) is inserted in several independent chunks, and each chunk produces its own batch group with its own total_customers. Do not rely on total_customers to equal the total size of the whole import — collect customers across requests and deduplicate on customerscore_id.


Verifying the signature

Every request includes an X-customerscore-HMAC header so you can confirm it came from Customerscore.io and was not tampered with. The value is:

sha256=<hex>

where <hex> is the HMAC-SHA256 of the raw request body (the exact bytes you received, before any JSON parsing), keyed with your webhook's secret key.

The verification steps and Node.js / Python examples are identical for every event — see Verifying Webhook Signatures in the overview.


Example payloads

Single customer created via the API

{
  "event": "customer.created",
  "timestamp": "2026-09-03T10:00:00.412Z",
  "data": {
    "source": "API",
    "customers": [
      {
        "customerscore_id": 98765,
        "external_id": "acme-001",
        "name": "Acme Corporation",
        "created_at": "2026-09-03T10:00:00.389Z"
      }
    ],
    "batch": {
      "current_batch": 1,
      "total_batches": 1,
      "total_customers": 1,
      "batch_size": 1
    }
  }
}

Batch created by a HubSpot sync

{
  "event": "customer.created",
  "timestamp": "2026-09-03T04:00:12.001Z",
  "data": {
    "source": "HUBSPOT",
    "customers": [
      {
        "customerscore_id": 98766,
        "external_id": "hs-1180234",
        "name": "Beta Industries",
        "created_at": "2026-09-03T04:00:11.870Z"
      },
      {
        "customerscore_id": 98767,
        "external_id": "hs-1180235",
        "name": "Gamma LLC",
        "created_at": "2026-09-03T04:00:11.870Z"
      }
    ],
    "batch": {
      "current_batch": 1,
      "total_batches": 1,
      "total_customers": 2,
      "batch_size": 2
    }
  }
}

Handling the event

A typical handler upserts the mapping between your ID and Customerscore.io's ID, then optionally enriches the record once scoring has run:

// express handler — signature already verified (see the overview)
async function onCustomerCreated(payload) {
  const { source, customers, batch } = payload.data;
 
  for (const c of customers) {
    await db.upsert("customerscore_customers", {
      customerscore_id: c.customerscore_id, // idempotency key
      external_id: c.external_id,
      name: c.name,
      created_at: c.created_at,
      source,
    });
  }
 
  console.log(
    `batch ${batch.current_batch}/${batch.total_batches}${batch.batch_size} new customers from ${source}`
  );
}