Skip to main content

Customer.io Connector

The Customer.io connector ingests email engagement events (opens, clicks, sends, bounces, unsubscribes) into GTM Clarity. It supports dual-mode ingestion -- real-time webhook push and historical API pull -- so you get both live event streaming and backfill capability.

Prerequisites

Before connecting Customer.io, you need:

  1. A Customer.io account with App API access (v2)
  2. A webhook reporting endpoint configured in Customer.io
  3. The environment variables listed below

Environment Variables

VariableDescription
CONNECTOR_ENCRYPTION_KEY32-byte hex string (64 hex chars) for AES-256-GCM API key encryption

Customer.io Settings (stored per-tenant)

SettingDescription
encryptedApiKeyAES-256-GCM encrypted Customer.io App API key
webhookSecretHMAC-SHA256 signing secret for webhook verification

Authentication

Customer.io uses API key authentication. The App API key is encrypted at rest using the same AES-256-GCM pattern as all other connectors:

  • Algorithm: aes-256-gcm
  • IV length: 12 bytes (randomly generated)
  • Storage format: base64(iv + tag + ciphertext)
  • Key: CONNECTOR_ENCRYPTION_KEY environment variable

API key validation is performed against the Customer.io region endpoint (https://api.customer.io/v1/accounts/region). Keys do not expire.

Data Flow

Ingestion Modes

Mode 1: Webhook Push (Real-time)

Customer.io sends events to your webhook endpoint as they occur. Each request is verified before processing.

Webhook Verification

The webhook handler verifies every incoming request using HMAC-SHA256:

  1. The raw request body (as Buffer) is hashed with the webhook secret
  2. The resulting hex digest is compared to the X-CIO-Signature header
  3. Comparison uses crypto.timingSafeEqual to prevent timing attacks
  4. Invalid signatures immediately throw an error (HTTP 403)
// Verification signature
verifyWebhookSignature(body: Buffer, signature: string, secret: string): boolean

Webhook Event Structure

FieldTypeDescription
event_idstringUnique event identifier
timestampnumberUnix timestamp (seconds)
event_typestringCustomer.io event type
customer_idstringCustomer.io customer identifier
email_addressstring?Recipient email
campaign_idstring?Campaign identifier
broadcast_idstring?Broadcast identifier
subjectstring?Email subject line
hrefstring?Clicked link URL

Mode 2: API Pull (Historical)

The API pull mode fetches historical campaign metrics from the Customer.io App API v2 for backfill and catch-up.

ParameterValue
Base URLhttps://api.customer.io/v2
Endpoint/activities?start={timestamp}&limit=100
Auth headerAuthorization: Bearer {apiKey}
PaginationCursor-based via next URL
Backfill window90 days on initial sync
Page size100 events
Rate limit handling15ms delay between pages (~66 req/s)
Rate limits

Customer.io has a soft limit of 100 requests per second. The connector paces at ~66 req/s with a 15ms inter-page delay, well under the limit.

Event Mapping

Customer.io events are mapped to canonical activity types:

Customer.io EventCanonical TypeChannel
email_sentemail_sentemail
email_openedemail_openemail
email_clickedemail_clickemail
email_bouncedemail_bouncedemail
email_unsubscribedemail_unsubscribedemail

Unmapped event types are silently skipped (not an error). The transform functions return null for unknown types.

Engagement scoring

The email_open and email_click types feed directly into the engagement scoring engine. Opens and clicks contribute to the email channel score with time-based decay.

Default Field Mappings

Source FieldTarget FieldTransformRequired
customer_idpersonExternalIdnoneYes
email_addressemaillowercaseNo
event_typetypenoneYes
timestampoccurredAtnoneYes
campaign_idproperties.campaignIdnoneNo
broadcast_idproperties.broadcastIdnoneNo
subjectproperties.subjectnoneNo
hrefproperties.hrefnoneNo

Canonical Activity Output

Every Customer.io event produces a CanonicalActivity with these properties:

{
externalId: "evt_abc123", // event_id from webhook or id from API
type: "email_open", // Mapped type
channel: "email", // Always "email"
occurredAt: new Date(1709500000000),// Unix timestamp * 1000
personExternalId: "cust_456", // customer_id
properties: {
source: "customerio",
emailAddress: "user@example.com",
campaignId: "campaign_789",
subject: "Your weekly digest",
// ... additional metadata
}
}

Setup Steps

  1. Generate an App API key in Customer.io under Settings > App API
  2. Navigate to Settings > Connectors in the GTM Clarity dashboard
  3. Click "Connect Customer.io" and paste your API key (it will be encrypted at rest)
  4. Configure the webhook in Customer.io:
    • Set the endpoint to https://your-domain.com/api/connectors/customerio/webhook
    • Copy the webhook signing secret into your connector settings
    • Select the email event types you want to forward
  5. Trigger initial sync to backfill 90 days of historical campaign metrics
  6. Verify webhook delivery by sending a test email and checking for activity records

Health Check

The health check validates the API key against the Customer.io region endpoint. A successful GET to https://api.customer.io/v1/accounts/region with a 200 response confirms the connector is healthy.

Troubleshooting

IssueCauseResolution
Webhook rejected (403)Invalid HMAC signatureVerify the webhook secret matches between Customer.io and connector settings
API pull returns emptyAPI key lacks App API scopeGenerate a new key with App API permissions
Missing eventsUnmapped event typeOnly email_sent, email_opened, email_clicked, email_bounced, and email_unsubscribed are mapped
Duplicate eventsBoth webhook and pull activeThis is expected and safe -- deduplication handles overlapping records via externalId