Customer.io webhooks: webhook actions and reporting webhooks are different tools

By·Published·Updated
Customer.io webhooks: webhook actions and reporting webhooks are different tools

In 1953 David Warren, a research scientist at the Aeronautical Research Laboratory in Melbourne, was working on the investigation into the crashes of the Comet, the world's first jet-powered commercial airliner. The Defence Science and Technology Group records that he had recently seen a miniature recorder at a trade fair. It struck him that a recording of the last minutes in the cockpit would have answered most of the investigators' questions.

The device he conceived could store up to four hours of cockpit speech alongside flight instrument readings. A report he circulated internationally drew little interest, so he built a demonstration model in his own time, which Australian Geographic dates to 1957. In 1958 Sir Robert Hardingham, a former British Air Vice-Marshal, saw it during an informal visit, and Warren and his recorder were on a flight to England almost immediately. There the Ministry of Aviation said fitting recorders might soon be made mandatory. Australia itself took no interest until a Fokker Friendship crashed at Mackay, Queensland, in 1960 and the inquiry judge recommended recorders in all airliners. Australia then became the first country to make cockpit voice recording compulsory.

A flight recorder does one job. It logs everything, for someone to read later. It is not the instrument a pilot uses to act on a single reading in flight. Customer.io has both kinds of instrument, and it calls them both webhooks. That is where the trouble starts.

TL;DR: Customer.io has two features called webhooks and they answer opposite questions. A webhook action is a step inside an automation. It sends a payload you write about one person to one API, retries up to 11 times over about an hour, and can write the response back onto that person. A reporting webhook is set up under Integrations, not in a campaign. It streams every event of the types you select, across the whole workspace, to one endpoint in Customer.io's own payload, with a 4-second timeout and retries over 7 days. You cannot scope it to one automation; the docs' own FAQ says as much. The rule: if the request is about what one person should experience next, it is a webhook action; if it is about what happened to every message, it is a reporting webhook. For metrics in a standard event shape, or data flowing into Customer.io, use Data Pipelines or the Track API instead.

Why does Customer.io have two things called webhooks?

Because both are HTTP callbacks, and that is the whole of what they share. The Standard Webhooks specification defines the word plainly: "Webhooks are a common name for HTTP callbacks, and are a way for services to notify each other of events." (Standard Webhooks).

The webhook action docs describe their feature in terms of what you can do to a person: "Webhooks let you pass data to, and return data from, just about any public API on the internet." (Customer.io docs, webhook actions). The reporting webhooks docs describe theirs in terms of what happened: "Reporting webhooks send real-time message activity events (e.g. sends, opens, clicks) as JSON in an HTTP POST." Near the top, that page sends anyone who wants a custom webhook from a workflow to the webhook actions page instead (Customer.io docs, reporting webhooks).

One pushes a decision out. The other streams a record out.

What a webhook action does: one person, one step, your payload

A webhook action is a block you drag into an automation workflow. When a person reaches it, Customer.io sends a request you have written. You choose the method from GET, POST, PUT, DELETE and PATCH. You set the headers, with Content-Type: application/json pre-filled. You write the body, and Liquid works inside it, so {{customer.id}} becomes that person's value at send time. Nothing is sent until you change the action's sending behaviour to Send automatically (webhook actions).

The response comes back to the person

This is what makes a webhook action a control rather than a logger. On the Response tab you map values from the API's reply onto the person, using the response key with dot notation. The docs' own example creates a lead in a CRM and writes the returned lead_id back onto the profile (webhook actions).

Customer attributes persist on the person. Journey attributes live only for that person's path through that automation and are deleted when it ends. They shipped on 22 December 2025, so webhook data need not sit in person attributes which don't expire and could clutter up your workspace (release note, 22 December 2025). The limits are 100 journey attributes per journey, a 128-byte name and a 100 KiB value (webhook actions).

When a webhook action sets attributes, the person waits at that step until the update completes or every retry has failed. A reporting webhook can never give you that, because it is not in anyone's path.

Timeouts, retries, rate and signing

Customer.io waits 16 seconds for your endpoint to respond. A timeout, or a 408, 409, 429 or 5xx response, triggers retries: up to 11 times over approximately 1 hour. If every retry fails, the attribute update is skipped and the person moves on anyway. The response body is capped at 100 KB (webhook actions).

There is no global rate limit. You can set one per automation on the webhook channel, from 1 to 60,000 requests per minute, shared across that automation (webhook actions). Since 5 December 2025 you can also hold people in a randomised delay before the step, to avoid overloading API endpoints with webhook requests (release note, 5 December 2025).

Every request carries an X-CIO-Signature header, an HMAC-SHA256 keyed with your signing key over the string v0:<timestamp>:<body>, where the body is the raw request body, plus an X-CIO-Idempotency-Key so a retry can be recognised and skipped. Since 17 May 2023 that signing key has been separate from the reporting webhook key (release note, 17 May 2023). Since 14 January 2026 a webhook's URL, headers and auth can be configured once and reused across automations. The release note's pitch: "without exposing API credentials to your whole team" (release note, 14 January 2026).

What a reporting webhook does: every event you select, workspace-wide, their payload

A reporting webhook is not in a campaign. You create it under Integrations > Reporting Webhooks, give it an endpoint URL, tick the events you want, and optionally set two switches. Send Frequency decides whether you receive an event the first time it happens or every time. Body Content adds the rendered message body to every sent event (reporting webhooks).

You do not write the payload. Customer.io does. Every event in the current format carries an object_type such as email, a metric such as clicked, and an event_id for deduplication. The data block holds the delivery_id, the person's identifiers including the immutable cio_id, and where they apply the campaign_id, campaign_name and campaign_tags (reporting webhooks).

Delivery works differently, on purpose

The timeout is 4 seconds, not 16. Miss it and Customer.io retries with exponential backoff over 7 days, queuing later events behind the failed one. And if you disable a reporting webhook and re-enable it, the docs are blunt: "We don't backfill or replay webhook events that occurred while the webhook was disabled." (reporting webhooks)

The one answer that decides most arguments

The reporting webhooks page carries its own FAQ, and one entry settles the question we hear most. "Can I specify which automation(s) get forwarded to an external webhook?" The answer is "No." Two more entries: by default only the first click on a link is sent, and no further rate limiting is available (reporting webhooks). Reporting webhooks can also be created, listed, updated and deleted over the App API, through the /v1/reporting_webhooks endpoints (reporting webhooks API reference).

Webhook action or reporting webhook: the side-by-side

Webhook action Reporting webhook
Where you set it up A step in an automation workflow Integrations > Reporting Webhooks
What fires it One person reaching the step Any message event in the workspace
Scope That person, that step Every automation, broadcast, newsletter and transactional message
Who writes the payload You, in JSON or form-encoded, with Liquid Customer.io: object_type, metric, data
Response handling Map response.* onto attributes; the person waits None; the endpoint acknowledges
Timeout 16 seconds 4 seconds
Retries Up to 11 over approximately 1 hour Exponential backoff over 7 days, then events expire
Rate control Per-automation limit, 1 to 60,000 a minute; random delays None you set; batches of roughly 40 per workspace
Filter to one campaign Inherent: it is in the campaign Not available; filter on campaign_id yourself

Figures from the two docs pages, both updated 14 September 2026.

The two failures we keep seeing

A webhook action per message to log deliveries

Every campaign gets a webhook action after each email that posts we sent email 3 to this person to a logging endpoint. Then a new campaign launches without the step, and the log has a hole nobody notices for a month.

The step records that a person reached it. It does not know whether the email was delivered, opened or bounced, because those events happen after the person has moved on. Retries stop after about an hour, so an outage at the logging endpoint loses records for good (webhook actions). A single reporting webhook already carries the delivery_id, campaign_id and campaign_name for every message in the workspace, bounces included, and retries for 7 days (reporting webhooks).

A reporting webhook someone tries to narrow to one campaign

A team wants to know when anyone clicks in one re-engagement campaign, so they point a reporting webhook at a Zapier catch hook or a Slack notifier and tick clicked. Within a day the channel is unusable: the feed carries every click the workspace sends.

Scoping is not available and filtering is your job. That is fine for code you own, and not fine for a no-code tool that charges per task or a person's Slack channel. In those cases use a webhook action inside the campaign, sending the small payload the recipient needs (reporting webhooks).

The decision rule: one person or every message

If the request is about what one person should experience next, it is a webhook action; if it is about what happened to every message, it is a reporting webhook.

Three requests we hear often:

  • Tell our billing system this person reached step 4. Webhook action. One person, one moment, your payload.
  • Feed our deliverability dashboard. Reporting webhook, because it is the only one that sees bounces. Read what the open flags mean for email client metrics before you trust the open counts.
  • Push a lead score to the CRM when a trial user reaches the pricing step of the onboarding campaign. Webhook action at that step. A reporting webhook would send the CRM every event in the workspace and leave it to work out who was on a trial.

If a reporting webhook consumer has to reconstruct which step of which journey a person was on, that logic belongs in the automation. The send-event pattern for cross-campaign architecture is the place to start.

When neither webhook is right

Message metrics in a standard event shape

If the destination is a warehouse, a product analytics tool or a customer data platform (CDP), a raw reporting webhook makes you maintain a consumer that understands Customer.io's payload. Data Pipelines, Customer.io's layer for moving data between sources and destinations, offers a documented alternative. It treats the workspace's message activity as a source, listed as Customer.io (Workspaces), and sends it to destinations as track events named by channel and metric, such as Email Opened. The messageId on each event is the reporting webhook's event_id. The docs note that anyone familiar with reporting webhooks will notice this format is different (Journeys message metrics source).

If you still want a URL rather than a named destination, the Data Pipelines webhook destination is a third instrument again. It triggers on identify or track calls or on specific events such as email_opened, and can batch up to 1,000 events into one request. It signs with a SHA1 HMAC in an X-Signature header rather than X-CIO-Signature, so do not verify it with the same code (webhook destination, updated 1 September 2026).

Our Data Pipelines versus Segment comparison sets out when that route earns its place. The integration guide sets all of these beside reverse ETL and the APIs.

Data coming into Customer.io

Neither webhook receives anything. The reporting webhooks FAQ asks "Is it possible to host a webhook endpoint in Customer.io?" and answers no: incoming data goes through the REST API or a Segment integration. The docs do describe one loop that uses every piece. A webhook action sends a message through a third-party provider, and a reporting webhook is pointed at that provider, which matches events by the X-CIO-Delivery-ID header on the message. The provider then posts delivered and bounced back through the Track metrics API (reporting webhooks).

Where this sits in a data plan

Each of these decisions is easy once the data plan says which way every piece of data travels. When we migrated AIRE Health from an internal custom email system to Customer.io, the data plan came before any automation was built. Campaign conversion rate rose from 7.0% to 17.8%. The plan is also where webhook payload field names get fixed. Our event schema guide for marketing teams handles the naming side.

Once live, verify signatures against the raw body: re-serialising with JSON.stringify() or json.dumps() before hashing can change the bytes and break the match (webhook actions). If your team scripts its setup, the CLI versus MCP decision guide shows where reporting webhooks fit. The lifecycle reporting and attribution guide picks up what to do with the feed once it lands.

Warren conceived his recorder in 1953, and Australia only made it compulsory after the Mackay inquiry of 1960; nobody proposed wiring it to the controls. Keep your two webhooks apart in the same way. If you have inherited a workspace where both are doing each other's job, our Customer.io agency team untangles this regularly. Send us the shape of the integration and we will tell you which instrument it is.

Frequently asked questions

What is the difference between a webhook action and a reporting webhook in Customer.io?

A webhook action is a step in an automation that sends your own payload about one person to an API. It can write the response back as an attribute (webhook actions). A reporting webhook is a workspace-level integration that sends Customer.io's payload for every event of the types you select to one endpoint (reporting webhooks).

Can I set up a Customer.io reporting webhook for just one campaign?

No: a reporting webhook cannot be limited to one campaign, and the documented workaround is to filter events by campaign_id on your own server (reporting webhooks).

How long does Customer.io retry a failed webhook?

A webhook action retries up to 11 times over approximately 1 hour after a 16-second timeout (webhook actions). A reporting webhook retries with exponential backoff over 7 days after a 4-second timeout, and events still pending after that expire (reporting webhooks).

Can a Customer.io webhook action update a person's attributes from the API response?

Yes: on the Response tab you map values from the reply, using response with dot notation, onto customer attributes that persist or journey attributes that are deleted when the journey ends. The person waits at that step until the update completes or all retries fail (webhook actions).

Do Customer.io webhook actions and reporting webhooks use the same signing key?

No: since 17 May 2023 webhook actions have had their own signing key under Workspace Settings, separate from the reporting webhook key under Integrations (release note, 17 May 2023).

Can I rate limit Customer.io webhooks?

Webhook actions, yes: set a limit on the webhook channel of an automation, from 1 to 60,000 requests a minute (webhook actions). Reporting webhooks, no: no further rate limiting is available beyond the default of one event per action (reporting webhooks).

Should I use reporting webhooks or Data Pipelines to get Customer.io message metrics into my warehouse?

Use Data Pipelines when the destination expects standard track events. The Customer.io (Workspaces) source sends message metrics as events such as Email Opened. That format differs from the reporting webhook payload, as the source page notes (Journeys message metrics source).

Sources

Want this working in your Customer.io workspace?

It's what we do all day for SMBs—strategy, automations, deliverability and hands-on execution.

See how we work as a Customer.io agency →
David Crowther
Book a free consultation →

On our call, you'll be speaking with David Crowther, founder of NerveCentral.

Our initial consultation is not a sales call—you'll talk, I'll listen and ask questions—then I'll come back to you within 48 hours with our best ideas on how to grow your business.