> This page is part of the [Customer.io documentation](https://docs.customer.io). For the complete index, see [llms.txt](https://docs.customer.io/llms.txt).
> Last updated: November 9, 2026
> To take actions in Customer.io from an agent, use the [Customer.io CLI](https://docs.customer.io/ai/cli/get-started.md) or the [MCP server](https://docs.customer.io/ai/mcp/get-started.md).
> This page has tabbed examples for: Deliveries, Delivery Content, Metrics, Outputs, People, Subjects, Attributes, Campaigns, Broadcasts, Actions, Objects, Object Types, Object Attributes, Events, Inbound. Add `?tab=<name>` to the URL to get only that tab, and repeat it for more than one, for example `?tab=deliveries&tab=delivery-content`.

# Databricks

Send Customer.io data about messages, people, metrics, and so on to Databricks by way of an Amazon S3, Google Cloud Storage, or Azure Blob Storage bucket. This integration syncs data roughly every 15 minutes, helping you keep up to date on your audience's message activities.

We have two integrations!

This integration uses Customer.io as a source, and syncs data from your Customer.io workspace to your data warehouse, including automation information. Our other integration sends data from multiple sources to your data warehouse. While the other integration captures data from multiple sources, even if those sources don’t send data to your workspace, it *cannot* capture some data from within Customer.io like automation information.

This page helps you send Customer.io data to Databricks. To import data from Databricks into Customer.io instead, see [Databricks reverse ETL](https://docs.customer.io/integrations/data-in/connections/reverse-etl/databricks/).

## How it works

This integration exports individual [parquet](https://parquet.apache.org/) files for **Deliveries**, **Metrics**, **Subjects**, **Outputs**, **Content**, **People**, and **Attributes** to your storage bucket. Each parquet file contains data that changed since the last export.

Once the parquet files are in your storage bucket, you can import them into data platforms like Fivetran or data warehouses like Redshift, BigQuery, and Snowflake.

Note that this integration only publishes parquet files to your storage bucket. You must set your data warehouse to ingest this data. There are many approaches to ingesting data, but it typically requires a COPY command to load the parquet files from your bucket. After you load parquet files, you should set them to expire to delete them automatically.

We attempt to export parquet files every 15 minutes, though actual sync intervals and processing times may vary. When syncing large data sets, or Customer.io experiences a high volume of concurrent sync operations, it can take up to several hours to process and export data. This feature is not intended to sync data in real time.

Customer.io doesn’t connect to Databricks directly. You’ll set up a Customer.io integration to send data to a storage bucket in the same cloud as your Databricks workspace, and then you’ll load the files from that bucket into Databricks.

If your Databricks workspace runs on

Set up the sync with

AWS

[Amazon S3](#s3-bucket)

Google Cloud Platform (GCP)

[Google Cloud Storage](#gcs-bucket)

Azure

[Azure Blob Storage](#azure-blob)

Your workspace can sync to only one storage service at a time.

Your initial sync includes historical data

During the first sync, you’ll receive a history of your Deliveries, Metrics, Subjects, and Outputs data. However, People who have been deleted or suppressed before the first sync are **not** included in the People file export and the historical data in the other export files is anonymized for the deleted and suppressed People.

### The initial export vs incremental exports

Your initial sync is a set of files containing historical data to represent your workspace’s current state. Subsequent sync files contain changesets.

*   **Metrics**: The initial metrics sync is broken up into files with two sequence numbers, as follows. `<name>_v5_<workspace_id>_<sequence1>_<sequence2>`.
*   **Attributes**: The initial Attributes sync includes a list of people and their current attributes. Subsequent files will only contain *attribute changes*, with one change per row.
*   **Events**: The initial events sync includes up to 30 days of past events. Subsequent files contain events since the previous sync interval. We cannot export events older than 30 days.

For example, let’s say you’ve enabled the [Attributes export](#schemas). We will attempt to sync your data to your storage bucket every 15 minutes:

1.  12:00pm We sync your Attributes Schema for the first time. This includes a list of people and their current attributes.
2.  12:05pm User1’s email is updated to [company-email@example.com](mailto:company-email@example.com).
3.  12:10pm User1’s email is updated to [personal-email@example.com](mailto:personal-email@example.com).
4.  12:15 We sync your data again. In this export, you would only see attribute changes, with one change per row. User1 would have one row dedicated to his email changing.

### Requirements

If you use a firewall or an allowlist, you must allow the following IP addresses to support traffic from Customer.io. Make sure you use the correct IP addresses for your account region.

US Region

EU Region

34.71.192.245

34.118.255.179

35.188.196.183

34.76.143.229

104.198.177.219

34.78.91.47

35.184.88.76

35.187.55.80

34.72.101.57

104.199.99.65

34.123.199.33

34.76.81.2

35.222.137.61

34.77.146.181

34.68.113.63

34.140.234.108

35.240.84.170

35.195.54.15

34.38.105.52

104.155.66.230

34.76.119.61

34.140.67.73

34.78.74.81

Do you use other Customer.io features?

These IP addresses are specific to outgoing Data Warehouse integrations. If you use your own SMTP server or receive webhooks, you may also need to allow additional addresses. See our [complete IP allowlist](https://docs.customer.io/accounts/security/ip-addresses/).

## Set up the sync with Amazon S3

Before you begin, make sure that you’re prepared to ingest relevant parquet files from Customer.io. For S3, you’ll need to set up your bucket with `ListBucketVersions`, `ListBucket`, `GetObject`, and `PutObject` before you can sync data from Customer.io.

1.  Create an Access Key and a Service Key with read/write permissions to your S3 or Yandex bucket.
2.  Go to **Integrations** and select Amazon S3 and then click **Sync Bucket**.
3.  Enter information about your bucket and click **Select data**.
    *   Enter the *Name* of your bucket.
    *   Enter the *path* to your bucket.
    *   Paste your *Access* and *Secret* keys in the appropriate fields.
    *   Select the *Region* your bucket is in.

 ![dws-s3-setup.png](https://docs.customer.io/images/dws-s3-setup.png)

4.  Select the data types that you want to export from Customer.io to your bucket. By default, we export all data types, but you can disable the types that you aren’t interested in.
5.  Click **Create and sync data**.

### Each sync includes a `cio-validate` file

If you sync data to an Amazon S3 bucket, Customer.io writes a file called `cio-validate` to your bucket before every sync. This is an empty file that we use to verify that we have write permissions to your bucket before each sync.

**You can safely delete this file**. It does not affect data sync operations, and it’s not part of your exported data.

If you have automated processes that import parquet files from your bucket, you may want to configure them to ignore the `cio-validate` file, since it’s not a parquet file and doesn’t contain any data.

## Set up the sync with Google Cloud Storage

Before you begin, make sure that you're prepared to ingest relevant parquet files from Customer.io.

To use a GCS storage bucket, you must set up a service account key (JSON) that grants read/write permissions to the bucket. You'll provide the contents of this key to Customer.io when you set up this integration.

1.  Go to **Integrations** and select Google Cloud Storage and then click **Sync your Google Cloud Storage bucket**.
2.  Enter information about your GCS bucket and click **Validate & select data**.
    *   Enter *Name* of your GCS bucket.
    *   Enter the *Path* to your GCS bucket.
    *   Paste the JSON of your *Service Account Key*.

 ![dws-gcs-bucket.png](https://docs.customer.io/images/dws-gcs-bucket.png)

3.  Select the data that you want to export from Customer.io to your bucket. By default, we export all data, but you can disable the types that you aren’t interested in.

4.  Click **Create and sync data**.

## Set up the sync with Azure Blob Storage

To sync to Azure Blob Storage, you create an access policy and a Shared Access Signature (SAS) URL for a container, and then you add the URL to the **Azure Blob Storage** integration in Customer.io. Follow the steps in [Set up an Azure Blob Storage integration](https://docs.customer.io/integrations/data-out/connections/ms-azure-data-out/#azure-blob).

Databricks can only read your container if its storage account has a hierarchical namespace, which makes it an Azure Data Lake Storage (ADLS) Gen2 account. Databricks’ Unity Catalog checks for a hierarchical namespace when you create an external location on Azure, and it doesn’t accept a standard Blob Storage account. When you create the storage account in Azure, turn on **Enable hierarchical namespace**. If you already have a standard account, you can [upgrade it to ADLS Gen2 ↗](https://learn.microsoft.com/en-us/azure/storage/blobs/upgrade-to-data-lake-storage-gen2-how-to).

You set up the sync for an ADLS Gen2 account with the same steps. When you copy the SAS URL, use the one with your account’s `blob.core.windows.net` address, not its `dfs.core.windows.net` address.

## Load the files into Databricks

After Customer.io writes files to your bucket, you load them into Databricks:

1.  Register the bucket path as a Unity Catalog [external location ↗](https://docs.databricks.com/aws/en/connect/unity-catalog/cloud-storage/external-locations) so that Databricks can read from it. Databricks’ docs cover each cloud: [AWS ↗](https://docs.databricks.com/aws/en/connect/unity-catalog/cloud-storage/external-locations), [GCP ↗](https://docs.databricks.com/gcp/en/connect/unity-catalog/cloud-storage/external-locations-gcs), and [Azure ↗](https://learn.microsoft.com/en-us/azure/databricks/connect/unity-catalog/cloud-storage/external-locations-adls).
2.  Load the Parquet files into tables with [Auto Loader ↗](https://docs.databricks.com/aws/en/ingestion/cloud-object-storage/auto-loader/), a [streaming table ↗](https://docs.databricks.com/aws/en/ldp/concepts/streaming-tables), or [COPY INTO ↗](https://docs.databricks.com/aws/en/ingestion/cloud-object-storage/copy-into/). Each sync adds new files to your bucket, about every 15 minutes, and leaves the earlier files in place. Auto Loader, streaming tables, and COPY INTO keep track of the files they’ve already loaded, so each run picks up only the new files.
3.  In your cloud provider’s console, add a lifecycle rule to your bucket that deletes files after a set number of days, so that loaded files don’t build up over time. Choose a period that’s longer than the time between your loads. Each cloud provider documents its lifecycle rules: [Amazon S3 ↗](https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lifecycle-mgmt.html), [Google Cloud Storage ↗](https://cloud.google.com/storage/docs/lifecycle), and [Azure Blob Storage ↗](https://learn.microsoft.com/en-us/azure/storage/blobs/lifecycle-management-overview).

Customer.io writes every data type to the same bucket path. To load each data type into its own table, match files by the start of their name, like `deliveries_` or `metrics_`. See [How are exported parquet files organized?](https://docs.customer.io/integrations/data-out/data-warehouses/data-warehouses-intro/#how-are-exported-parquet-files-organized) for the filename format, and the [schemas](#schemas) below for the files you can export.

## Pausing and resuming your sync

You can turn off files you no longer want to receive, or pause them momentarily as you update your integration, and turn them back on. When you turn a file schema on, we send files to catch you up from the last export.If you haven’t exported a particular file before—the file was never “on”—the initial sync contains your historical data.

You can also disable your entire sync, in which case we’ll quit sending files all together. When you enable your sync again, we send all of your historical data as if you’re starting a new integration. Before you disable a sync, consider if you simply want to [disable individual files](#turn-data-types-on-or-off) and resume them later.

Delete old sync files before you re-enable a sync

Before you resume a sync that you previously disabled, you should clear any old files from your storage bucket so that there’s no confusion between your old files and the files we send with the re-enabled sync.

### Disabling and enabling individual export files

1.  Go to *Data & Integrations* > *Integrations* and select the storage integration your sync uses: **Amazon S3**, **Google Cloud Storage**, or **Azure Blob Storage**.

2.  Select the files you want to turn on or off.

When you enable a *file*, the next sync will contain baseline historical data catching up from your previous sync or the complete history if you haven’t synced a file before; subsequent syncs will contain changesets.

Turning the People file off

If you turn the People file off for more than 7 days, you will not be able to re-enable it. You’ll need to delete your sync configuration, purge all sync files from your destination storage bucket, and [create a new sync](#set-up-a-database-integration) to resume syncing people data.

 ![Turn files on or off](https://docs.customer.io/images/dws-turn-on-sync.png)

### Disconnecting your sync

Disconnecting deletes your sync and stops all exports. If you only want to stop some data types for a while, [turn off individual files](#turn-data-types-on-or-off) instead.

If you set up a sync again later, Customer.io sends all of your historical data again, as if you were starting a new integration. Your Databricks tables already hold that data, so loading the resent files into the same tables creates duplicate rows. When you reconnect, set up the new sync with a new bucket path. Then load the new files into new tables, or empty your existing tables before you load them. If you use Auto Loader or a streaming table, point it at the new path with a new checkpoint or a full refresh, so that it loads every file from the new sync.

1.  Go to *Data & Integrations* > *Integrations* and select the storage integration your sync uses.
2.  Click **Disconnect sync** and confirm.

## Manage your configuration

You can change settings for a bucket, if your path changes or you need to swap keys for security purposes.

1.  Go to *Data & Integrations* > *Integrations* and select the storage integration your sync uses.
2.  Click **Manage configuration…**.
3.  Make your changes. Customer.io doesn’t show your saved credential, so you need to enter it again every time you update the configuration: your *Secret key* for Amazon S3, your *Service account key* for Google Cloud Storage, or your *Blob SAS URL* for Azure Blob Storage.
4.  Click **Update configuration**. Subsequent syncs use your new configuration.

### Update sync schema version

Before you prepare to update your data warehouse sync version, see the [changelog](https://docs.customer.io/integrations/data-out/data-warehouses/data-warehouses-intro/#changelog). [You’ll need to update schemas](https://docs.customer.io/integrations/data-out/data-warehouses/data-warehouses-intro/#upgrade-to-v9) to upgrade to the latest version (v9).

When updating from v1 to a later version, you must:

*   Update ingestion logic to accept the new file name format: `<name>_v<x>_<workspace_id>_<sequence>.parquet`
*   Delete existing rows in your Subjects and Outputs tables. When you update, we send all of your Subjects and Outputs data from the beginning of your history using the new file schema.

 ![dws-upgrade-version.png](https://docs.customer.io/images/dws-upgrade-version.png)

1.  Go to *Data & Integrations* > *Integrations* and select the storage integration your sync uses.
2.  Click **Upgrade schema version**.
3.  Follow the instructions to make sure that your ingestion logic is updated accordingly.
4.  Check **I’ve made all changes and am ready to upgrade** and click **Upgrade sync**. The next sync uses the updated schema version.

## Parquet file schemas

This section describes the different kinds of files you can export from our Database-out integrations. Many schemas include an `internal_customer_id`—this is the `<Tooltip term="cio_id" />`. You can use it to resolve a person associated with a subject, delivery, etc.

These schemas represent the latest versions available. Check out our [changelog](https://docs.customer.io/integrations/data-out/data-warehouses/data-warehouses-intro/#changelog) for information about earlier versions.

<!-- tabs -->

<!-- tab: Deliveries -->

#### Deliveries

Deliveries are individual email, in-app, push, SMS, slack, and webhook records sent from your workspace. The first deliveries export file includes baseline historical data. Subsequent files contain rows for data that changed since the last export.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the delivery record.

`delivery_id` primary key

string (UTF8)

✅

The ID of the delivery record.

`internal_customer_id` foreign key: People

string (UTF8)

—

The `cio_id` of the person in question. Use the `people` parquet file to resolve this ID to an external `customer_id` or email address.

`subject_id` foreign key: Subjects

string (UTF8)

—

If the delivery was created as part of a Campaign or API Triggered Broadcast workflow, this is the ID for the path the person went through in the workflow. **Note: This value refers to, and is the same as, the `subject_name` in the subjects table.**

`event_id` foreign key: Subjects

string (UTF8)

—

If the delivery was created as part of an event-triggered Campaign, this is the ID for the unique event that triggered the workflow. Note that this is a foreign key for the subjects table, and not the metrics table.

`delivery_type`

string (UTF8)

✅

The type of delivery.

Accepted values: `email` · `webhook` · `sms` · `slack` · `push` · `in_app` · `line` · `inbox` · `whatsapp` · `live_notification`

`campaign_id`

integer (INT32)

—

If the delivery was created as part of a Campaign or API Triggered Broadcast workflow, this is the ID for the Campaign or API Triggered Broadcast.

`action_id`

integer (INT32)

—

If the delivery was created as part of a Campaign or API Triggered Broadcast workflow, this is the ID for the unique workflow item that caused the delivery to be created.

`newsletter_id`

integer (INT32)

—

If the delivery was created as part of a Newsletter, this is the unique ID of that Newsletter.

`content_id`

integer (INT32)

—

If the delivery was created as part of a Newsletter split test, this is the unique ID of the Newsletter variant.

`trigger_id`

integer (INT32)

—

If the delivery was created as part of an API Triggered Broadcast, this is the unique trigger ID associated with the API call that triggered the broadcast.

`created_at`

datetime (TIMESTAMP\_MILLIS)

✅

The timestamp the delivery was created at.

`transactional_message_id` added in v2

integer (INT32)

—

If the delivery occurred as a part of a transactional message, this is the unique identifier for the API call that triggered the message.

`seq_num` added in v3

integer (INT64)

✅

A monotonically increasing number indicating relative recency for each record: the larger the number, the more recent the record.

`internal_object_id` foreign key: Objects added in v6

string (UTF8)

—

For object- or relationship-triggered campaigns, the internal ID of the object that triggered the delivery. Use the `objects` parquet file to resolve this ID to the external `object_id` (the ID you see in the Customer.io UI). Null for deliveries not triggered by an object.

`object_type_id` foreign key: Object Types added in v6

integer (INT32)

—

For object- or relationship-triggered campaigns, the ID of the object type that triggered the delivery. Use the `object_types` parquet file to resolve this ID to the object type name. Null for deliveries not triggered by an object.

`related_delivery_ids` added in v8

array (LIST)

✅

For push deliveries that match multiple devices for a profile, this is the list of the individual delivery IDs created for each device. Empty for deliveries that don't match multiple devices.

`app_id` added in v8

integer (INT32)

—

For push deliveries, the ID of the application that the push delivery was sent from. Null for non-push deliveries.

`conversation_operation` added in v10

string (UTF8)

—

For live notification deliveries, the operation this delivery performed on the conversation: `start`, `update`, or `end`. These are the same values your API and SDK payloads use. Deliveries on every other channel are null.

`holdout` added in v11

boolean

—

Whether Customer.io held the message back instead of sending it. A holdout still generates a record, which lets you compare a [holdout test](https://docs.customer.io/messaging/send/workflows/holdout-test/) against the people who received the message. Customer.io sets a value on every delivery row. The column can be null, so create it as nullable in your database.

<!-- /tab -->

<!-- tab: Delivery Content -->

#### Delivery Content

The `delivery_content` schema represents message contents; each row corresponds to an individual delivery. Use the `delivery_id` to find more information about the contents of a message, or the `recipient` to find information about the person who received the message.

If your delivery was produced from a campaign, it’ll include campaign and action IDs, and the newsletter and content IDs will be null. If your delivery came from a one-time send, the row will include newsletter and content IDs, and the campaign and action IDs will be null.

**Delivery content might lag behind other tables by 15-30 minutes (or roughly 1 sync operation).** We package delivery contents on a 15 minute interval, and can export to your data warehouse up to every 15 minutes. If these operations don’t line up, we might occasionally export `delivery_content` after other tables.

Delivery content can be a very large data set

Workspaces that have sent many messages may have hundreds or thousands of GB of data.

Delivery content is available in v4 or later

The `delivery_content` schema was introduced in our v4 release. You need to [update your data warehouse schemas](https://docs.customer.io/integrations/data-out/data-warehouses/data-warehouses-intro/#upgrade-to-v4) or later to take advantage of the update and see Delivery Content, Subjects, and Outputs.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the output record.

`delivery_id` primary key foreign key: Deliveries

string (UTF8)

✅

The ID of that delivery associated with the message content.

`campaign_id`

integer (INT32)

—

The ID for the campaign that produced the content (if applicable).

`action_id`

integer (INT32)

—

The ID for the campaign workflow item that produced the content.

`newsletter_id`

integer (INT32)

—

If the content was created as part of a newsletter, the unique ID of that newsletter.

`content_id`

integer (INT32)

—

If the content was created as part of a newsletter split test, the unique ID of the newsletter variant.

`from`

string (UTF8)

—

The From address for an email, if the content is related to an email.

`reply_to`

string (UTF8)

—

The Reply-To address for an email, if the content is related to an email.

`bcc`

string (UTF8)

—

The Blind Carbon Copy (BCC) address for an email, if the content is related to an email.

`cc`

string (UTF8)

—

The Carbon Copy (CC) address for an email, if the content is related to an email.

`recipient`

string (UTF8)

—

The person who received the message, dependent on the type. For an email, this is an email address; for an SMS, it's a phone number; for a push notification, it's a device ID.

`subject`

string (UTF8)

—

The subject line of the message, if applicable; required if the message is an email

`body`

string (UTF8)

—

The rendered body of the message. For an email, this is the HTML body.

`body_amp`

string (UTF8)

—

The HTML body of an email including any AMP-enabled JavaScript included in the message.

`body_plain`

string (UTF8)

—

The plain text of an email message, without HTML tags or AMP content. This field is typically `null` *unless* you manually set or change the plain-text version of an email (the `body_plain` field when you use our APIs).

`preheader`

string (UTF8)

—

"Also known as "preview text", this is the block block of text that users see next to, or underneath, the subject line in their inbox.

`url`

string (UTF8)

—

If the delivery is an outgoing webhook, this is the URL of the webhook.

Format: `uri`

`method`

string (UTF8)

—

If the delivery is an outgoing webhook, this is the HTTP method used—POST, PUT, GET, etc.

`headers`

string (UTF8)

—

If the delivery is an outgoing webhook, these are the headers included with the webhook.

`type`

string (UTF8)

—

The delivery type.

Accepted values: `email` · `webhook` · `sms` · `slack` · `push` · `in_app` · `line` · `inbox` · `whatsapp` · `live_notification`

<!-- /tab -->

<!-- tab: Metrics -->

#### Metrics

Metrics exports detail events relating to deliveries (e.g. messages sent, opened, etc). Your initial metrics export contains baseline historical data, broken up into files with two sequence numbers, as follows: `<name>_v5_<workspace_id>_<sequence1>_sequence2>`.

Subsequent files contain rows for data that changed since the last export.

The metric that the Customer.io UI calls *suppressed* appears in this export as `dropped`. Both names refer to the same thing: a message that wasn’t sent because the recipient was suppressed—for example, an email address on the suppression list after a prior hard bounce or spam complaint, or an SMS recipient who opted out of the sender. The specific cause may appear in the `reason` column. Messages blocked for other reasons—the person unsubscribed from email, hit a [message limit](https://docs.customer.io/messaging/send/message-limits/overview/), or was deleted—appear as `undeliverable` instead. A `dropped` metric does *not* indicate that a person is part of a holdout or control group. To identify branch or cohort membership, see [Identify which branch or cohort a person followed](https://docs.customer.io/integrations/data-out/data-warehouses/data-warehouses-intro/#identify-branch-membership).

You might have multiple entries per delivery\_id

For example, a person can click a link in a message multiple times, creating multiple “clicked” metrics. We might attempt a message delivery multiple times before it’s successfully sent, creating multiple “attempted” metrics. Depending on the metrics you care about, you might need to deduplicate or aggregate metrics based on the `delivery_id` to get correct counts.

Field name

Type

Required

Description

`event_id` primary key

string (UTF8)

✅

The unique ID of the metric event. This can be useful for deduplicating purposes.

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the metric record.

`delivery_id` foreign key: Deliveries

string (UTF8)

✅

The ID of the delivery record.

`metric`

string (UTF8)

✅

The type of metric. The `dropped` value is what the Customer.io UI calls *suppressed*—a message that wasn't sent because the recipient was suppressed. It does not indicate holdout or control-group membership.

Accepted values: `failed` · `dropped` · `sent` · `spammed` · `bounced` · `delivered` · `triggered` · `opened` · `clicked` · `converted` · `unsubscribed` · `attempted` · `undeliverable` · `deferred` · `device_change` · `drafted`

`created_at`

datetime (TIMESTAMP\_MILLIS)

✅

The timestamp the metric was created at.

`reason`

string (UTF8)

—

For certain metrics (e.g. attempted), the reason behind the action.

`link_id`

integer (INT32)

—

For "clicked" metrics, the unique ID of the link being clicked.

`link_url`

string (UTF8)

—

For "clicked" metrics, the URL of the clicked link.

Max length: 1000 bytes

Format: `uri`

`seq_num` added in v3

integer (INT64)

✅

A monotonically increasing number indicating relative recency for each record: the larger the number, the more recent the record.

`domain` added in v5

string (UTF8)

—

For email metrics, the sender's domain (the part after @ in the from address). If the from address isn't available or can't be parsed, this value is null.

`proxied` added in v5

boolean

✅

For email `opened` metrics, this indicates that the open event originated from a proxy server. For example, a proxy server may record an open independently of a message reaching the user's inbox. For other metrics, this is `false`.

`prefetched` added in v5

boolean

✅

For email `opened` metrics, this indicates that the metric was the result of prefetching and not necessarily a user action. For example, Gmail prefetches images to speed up rendering in the inbox, which may result in an opened metric—but the user didn't actually open the email. For other metrics, this this value is false.

`machine` added in v5

boolean

✅

For email `clicked` metrics, it means that the click event originated a non-human, e.g. a security service or email-protection application clicked a link. For other metrics, this is `false`.

`email_client` added in v5

string (UTF8)

—

For email metrics, the email client the recipient used, detected from the event's User-Agent—in practice we capture it on `opened` events. For non-email channels, and when we can't detect a client, this value is null. Values are uppercase, underscore-separated names: `GMAIL`, `YAHOO`, `APPLE_MAIL`, `APPLE_MAIL_PROTECTED` (an open through Apple Mail Privacy Protection's proxy), `SUPERHUMAN`, `THUNDERBIRD`, `LOTUS_NOTES`, `AOL_DESKTOP`, `OUTLOOK_2016`, `OUTLOOK_2013`, `OUTLOOK_2010`, `OUTLOOK_2007`, `OUTLOOK_2003`, `OUTLOOK_IOS`, `SAMSUNG_MAIL`, `POSTBOX`, `IPHONE`, `IPAD`, `ANDROID`, `BLACKBERRY`, `WINDOWS_PHONE`, and `BOT` (a known security scanner, not a person). Opens from a web browser are recorded as `WINDOWS_<browser>` or `MAC_<browser>`—for example, `WINDOWS_CHROME` or `MAC_SAFARI`. The set can grow as we recognize new clients. The Customer.io UI displays the same values under friendlier names—for example, `APPLE_MAIL_PROTECTED` appears as *Apple Mail Protected*.

`user_agent` added in v5

string (UTF8)

—

The user agent string of the person (or machine) who performed the action, where available. If we don't have a user agent string, this value is null.

`inbox_domain` added in v5

string (UTF8)

—

For email metrics, the inbox domain of the person who performed the action. If this value isn't discernable, or the metric is not email related, this value is null.

`inbox_provider` added in v5

string (UTF8)

—

For email metrics, the inbox provider of the person who performed the action. If this value isn't discernable, or the metric is not email related, this value is null.

`mx_host` added in v5

string (UTF8)

—

For email metrics, this is the MX host of the inbox (e.g. mailhost1.example.com). If this value isn't discernable, or the metric is not email related, this value is null.

`channel_provider` added in v10

string (UTF8)

—

The provider that sent the message, like your email or SMS vendor. Customer.io resolves the provider when it sends the message and attaches it to the `sent` metric alone. Later metrics for the same delivery—`delivered`, `bounced`, `clicked`, and so on—arrive from the provider's webhooks without it. To attribute those rows to a provider, join them back to the delivery's `sent` row on `delivery_id`.

<!-- /tab -->

<!-- tab: Outputs -->

#### Outputs

Outputs are the unique steps within each workflow journey. The first outputs file includes historical data. Subsequent files contain rows for data that changed since the last export.

Upgrade to v4 to use subjects and outputs

We’ve made some minor changes to subjects and outputs a part of our v4 release. If you’re using a previous schema version, we disabled your subjects and outputs on October 31st, 2022. You need to [upgrade](https://docs.customer.io/integrations/data-out/data-warehouses/data-warehouses-intro/#upgrade-to-v4) to schema version 4 or later, to continue syncing outputs and subjects data.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the output record.

`output_id` primary key

string (UTF8)

✅

The ID for the step of the unique path a person went through in a Campaign or API Triggered Broadcast workflow.

`subject_name` foreign key: Subjects

string (UTF8)

✅

A secondary unique ID for the path a person took through a campaign or broadcast workflow.

`output_type`

string (UTF8)

✅

The type of step a person went through in a Campaign or API Triggered Broadcast workflow. Note that the "delay" output\_type covers many use cases: a Time Delay or Time Window workflow item, a "[grace period](https://docs.customer.io/journeys/send/campaigns/grace-periods/)", or a date-based campaign trigger.

`action_id`

integer (INT32)

✅

The ID for the unique workflow item associated with the output.

`explanation`

string (UTF8)

✅

The explanation for the output.

`delivery_id` foreign key: Deliveries

string (UTF8)

—

If a delivery resulted from this step of the workflow, this is the ID of that delivery.

`draft`

boolean

—

If a delivery resulted from this step of the workflow, this indicates whether the delivery was created as a draft.

`link_tracked`

boolean

—

If a delivery resulted from this step of the workflow, this indicates whether links within the delivery are configured for tracking.

`split_test_index`

integer (INT32)

—

If the step of the workflow was a Split Test, this indicates the variant of the Split Test.

`delay_ends_at`

datetime (TIMESTAMP\_MILLIS)

—

If the step of the workflow involves a delay, this is the timestamp for when the delay will end.

`branch_index`

integer (INT32)

—

If the step of the workflow was a T/F Branch, a Multi-Split Branch, or a Random Cohort Branch, this indicates the branch or cohort that was followed, as an integer index corresponding to its position in the automation's workflow. There's no fixed convention for which index is a holdout or control group, and branch rows never include a `delivery_id`. For a Random Cohort Branch, `branch_label` gives you the cohort's name; other branch types export the index alone.

`manual_segment_id`

integer (INT32)

—

If the step of the workflow was a Manual Segment Update, this is the ID of the Manual Segment involved.

`add_to_manual_segment`

boolean

—

If the step of the workflow was a Manual Segment Update, this indicates whether a person was added or removed from the Manual Segment involved.

`created_at`

datetime (TIMESTAMP\_MILLIS)

✅

The timestamp the output was created at.

`seq_num`

integer (INT64)

✅

A monotonically increasing number indicating relative recency for each record: the larger the number, the more recent the record.

`holdout` added in v11

boolean

—

Whether Customer.io held the message back instead of sending it. Only delivery outputs carry a value. Every other output type is null rather than false, so a row for a holdout test/message doesn't read the same as a message that was sent.

`branch_label` added in v11

string (UTF8)

—

For a Random Cohort Branch, the name of the cohort the person went down. Customer.io resolves the name when it exports the row, so a cohort you rename shows its new name on rows exported after the change while earlier rows keep the old one. It's null for other branch types and for cohorts you haven't named.

<!-- /tab -->

<!-- tab: People -->

#### People

The first People export file includes a list of current people at the time of your first sync (deleted or suppressed people are not included in the first file). Subsequent exports include people who were created, deleted, or suppressed since the last export.

When you select the data you want to sync, this export appears as **People** in Customer.io. The exported files and tables keep the `people` naming.

People exports come in two different files (`v<x>` is your configured schema version—for example, `v9`):

*   `people_v<x>_<env>_<seq>.parquet`: Contains new people.
*   `people_v<x>_chgs_<env>_<seq>.parquet`: Contains changes to people since the previous sync.

These files have an identical structure and a part of the same data set. You should import them to the same table.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the person.

`customer_id`

string (UTF8)

✅

The ID of the person in question. This will match the ID you see in the Customer.io UI.

`internal_customer_id` primary key

string (UTF8)

✅

The `cio_id` of the person in question. Use the `people` parquet file to resolve this ID to an external `customer_id` or email address.

`deleted`

boolean

✅

This indicates whether the person has been deleted.

`suppressed`

boolean

✅

This indicates whether the person has been suppressed.

`created_at`

datetime (TIMESTAMP\_MILLIS)

✅

The date/time when the person was added to Customer.io (using the `_created_in_customerio_at` attribute). Note that this is not necessarily the same as a person's `created_at` value! If you import people from an external system, a CSV, or backdate the `created_at` value, this value is likely to be different from a person's `created_at` attribute.Note that this value is 0 for deleted or suppressed people

`updated_at` added in v3

datetime (TIMESTAMP\_MILLIS)

✅

The date-time when a person was updated. Use the most recent updated\_at value for a customer\_id to disambiguate between multiple records.

`email_addr` added in v3

string (UTF8)

✅

The email address of the person. For workspaces using email as a unique identifier, this value may be the same as the `customer_id`.

Format: `email`

`phone` added in v9

string (UTF8)

✅

The phone number of the person, taken from the person's `phone` attribute. For workspaces using phone number as a unique identifier, this value may be the same as the `customer_id`. It's empty when your workspace doesn't have phone enabled as an identifier, or when the person has no phone number.

<!-- /tab -->

<!-- tab: Subjects -->

#### Subjects

Subjects are the unique workflow journeys that people take through Campaigns and API-triggered broadcasts. The first subjects export file includes baseline historical data. Subsequent files contain rows for data that changed since the last export.

Upgrade to v4 to use subjects and outputs

We’ve made some minor changes to subjects and outputs a part of our v4 release. If you’re using a previous schema version, we disabled your subjects and outputs on October 31st, 2022. You need to [upgrade](https://docs.customer.io/integrations/data-out/data-warehouses/data-warehouses-intro/#upgrade-to-v4) to schema version 4 or later, to continue syncing outputs and subjects data.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the subject record.

`subject_name` primary key

string (UTF8)

✅

A unique ID for the path a person took through a campaign or broadcast workflow.

`internal_customer_id` foreign key: People

string (UTF8)

—

The `cio_id` of the person in question. Use the `people` parquet file to resolve this ID to an external `customer_id` or email address.

`campaign_type`

string (UTF8)

✅

The type of Campaign (segment, event, or triggered\_broadcast)

`campaign_id`

integer (INT32)

✅

The ID of the Campaign or API Triggered Broadcast.

`event_id` foreign key: Metrics

string (UTF8)

—

The ID for the unique event that triggered the workflow.

`trigger_id`

integer (INT32)

—

If the delivery was created as part of an API Triggered Broadcast, this is the unique trigger ID associated with the API call that triggered the broadcast.

`started_campaign_at`

datetime (TIMESTAMP\_MILLIS)

✅

The timestamp when the person first matched the campaign trigger. For event-triggered campaigns, this is the timestamp of the trigger event. For segment-triggered campaigns, this is the time the user entered the segment.

`created_at`

datetime (TIMESTAMP\_MILLIS)

✅

The timestamp the subject was created at.

`seq_num`

integer (INT64)

✅

A monotonically increasing number indicating relative recency for each record: the larger the number, the more recent the record.

<!-- /tab -->

<!-- tab: Attributes -->

#### Attributes

Attribute exports represent changes to people (by way of their attribute values) over time. The initial Attributes export includes a list of people and their current attributes. Subsequent files contain *attribute changes*, with one change per row.

For changes to nested attributes, like the [subscription preferences attribute](https://docs.customer.io/messaging/channels/subscriptions/manage-subscription-preferences/#backfill-preferences), the `attribute_name` will be the top-level attribute and the `attribute_value` returns the stringified JSON representing the nested changes. Using our subscription preferences example, the `attribute_name` would be `cio_subscription_preferences` and the `attribute_value` would be something like `"{\"topics\":{\"topic_7\":false,\"topic_8\":false}}"`.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the person.

`internal_customer_id` primary key

string (UTF8)

✅

The `cio_id` of the person in question. Use the `people` parquet file to resolve this ID to an external `customer_id` or email address.

`attribute_name`

string (UTF8)

✅

The attribute that was updated.

`attribute_value`

string (UTF8)

✅

The new value of the attribute.

`timestamp`

datetime (TIMESTAMP\_MILLIS)

✅

The timestamp of the attribute update.

<!-- /tab -->

<!-- tab: Campaigns -->

#### Campaigns

When you enable the **Campaign Metadata** schema, we actually return two different tables: *Campaigns* and *Actions*. The *Campaigns* table returns the names and versions of your campaigns and API-triggered broadcasts. Some other tables—like *Deliveries* and *Subjects*—return campaign *ID* values. You can use this table to get campaign names based on those IDs so you can better understand exports related to campaigns.

Note that this table includes both **Campaigns** and **API-triggered broadcasts**; both have `campaign_id` values. **One-time sends** appear in the Broadcasts table with a `broadcast_id`.

With each sync, we’ll return the rows where the `version` changed. The `version` is a number that increments each time a campaign or API-triggered broadcast is updated. This way, you can keep your campaign names and versions up-to-date.

Each row is an update

You’ll see a row for each update to each campaign or API-triggered broadcast. If joining to this table, you may want to include a condition so that you only get the MAX `updated_at` value for each `campaign_id` to get the most recent version.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace containing the campaign.

`campaign_id` primary key

integer (INT32)

✅

The ID of the campaign or API-triggered broadcast. Note that newsletters appear in the Broadcasts schema with a `broadcast_id`, not here.

`name`

string (UTF8)

✅

The name of a campaign. You set this in Customer.io when you create your campaign.

`topic_names` added in v5

array (LIST)

✅

LIST (Nullable). If you use our subscription center feature, this value is a comma-separated list of subscription topics that users must be subscribed to in order to receive the campaign. If the campaign is not associated with any topics, this value is null.

`tag_names` added in v7

array (LIST)

✅

The tags applied to the campaign, resolved to their names. Empty when the campaign has no tags.

`status` added in v7

string (UTF8)

✅

The campaign's current state: `draft`, `running`, `paused`, `stopped`, or `archived`. You might also catch a transitional state while a change is in progress—`evaluating`, `stopping`, `sunsetting`, `archiving`, or `deleting`. A campaign whose state isn't set yet reports `unknown`.

`type` added in v7

string (UTF8)

✅

The campaign type, which reflects how the campaign is triggered—for example, `segment`, `event`, or `date`. These use the same labels as the *journeys* export, so you can join the two tables on `type`.

Accepted values: `segment` · `seg_attr` · `rel_seg_attr` · `event` · `form` · `triggered_broadcast` · `none` · `date` · `webhook` · `relationship` · `object`

`description` added in v8

string (UTF8)

✅

The description text provided for the campaign. Empty when the campaign has no description.

`created_at`

datetime (TIMESTAMP\_MILLIS)

✅

The date-time (in milliseconds) when you created the campaign. You can create campaigns without activating them!

`updated_at`

datetime (TIMESTAMP\_MILLIS)

✅

The date-time (in milliseconds) when a campaign was last updated.

`version`

integer (INT64)

✅

An incrementing number starting at 1 representing the "version" of the campaign. The largest version number represents the latest version of the campaign. Versions increment when you change the name, trigger, or goal of a campaign. See the *Actions* table for changes to messages and other items in your campaign workflow.

<!-- /tab -->

<!-- tab: Broadcasts -->

#### Broadcasts

The **Broadcasts** schema returns information about your one-time sends. Note that API-triggered broadcasts appear in the **Campaigns** schema, not the Broadcasts schema. The initial sync returns all your one-time sends. Subsequent syncs return only the one-time sends that have changed since the last sync.

Each row is an update

You’ll see a row for each update to each broadcast. For example, if you edit the content, audience, and settings for a broadcast, you’ll see three rows. If joining to this table, you may want to include a condition so that you only get the MAX `updated_at` value for each `broadcast_id` to get the most recent version.

Broadcasts vs Campaigns

In the data warehouse schemas:

*   **One-time sends** appear in the Broadcasts schema with a `broadcast_id`
*   **API-triggered broadcasts** appear in the Campaigns schema with a `campaign_id`

This is why one-time sends and API-triggered broadcasts can share the same ID value—they exist in different schemas.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace containing the broadcast.

`broadcast_id` primary key

integer (INT32)

✅

The ID of the newsletter. Note that API-triggered broadcasts appear in the Campaigns schema with a `campaign_id`, not here.

`name`

string (UTF8)

✅

The name of a broadcast. You set this in Customer.io when you create your broadcast.

`topic_names` added in v5

array (LIST)

✅

LIST (Nullable). If you use our subscription center feature, this value is a comma-separated list of subscription topics that users must be subscribed to in order to receive the campaign. If the campaign is not associated with any topics, this value is null.

`tag_names` added in v8

array (LIST)

✅

The tags applied to the broadcast, resolved to their names. Empty when the broadcast has no tags.

`created_at`

datetime (TIMESTAMP\_MILLIS)

✅

The date-time (in milliseconds) when you created the broadcast. You can create broadcasts without activating them!

`updated_at`

datetime (TIMESTAMP\_MILLIS)

✅

The date-time (in milliseconds) when a broadcast was last updated.

<!-- /tab -->

<!-- tab: Actions -->

#### Actions

When you enable the **Campaign Metadata** schema, we actually return two different tables: *Campaigns* and *Actions*. The *Actions* table returns the names and versions of workflow steps in your campaigns, which we call actions . Some other tables—like *Deliveries* and *Subjects*—return action *ID* values. You can use this table to get the names of actions in your campaigns, so it’s easier for you to understand your campaign and action-related data.

With each sync, we’ll return the rows where the `version` changed. The `version` is a number that increments each time a campaign is updated. This way, you can keep your understanding of campaign actions up-to-date.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace containing the workflow action.

`campaign_id` foreign key: Campaigns

integer (INT32)

✅

The ID of the campaign containing the action.

`action_id`

integer (INT32)

✅

The ID of the action.

`name`

string (UTF8)

✅

The name of a workflow action. You set this in Customer.io when you create or edit your action. If you didn't set a name for the action, this field is empty.

`topic_names` added in v5

array (LIST)

✅

LIST (Nullable). If you use our subscription center feature, this value is a comma-separated list of subscription topics that users must be subscribed to in order to receive the campaign. If the campaign is not associated with any topics, this value is null.

`created_at`

datetime (TIMESTAMP\_MILLIS)

✅

The date-time (in milliseconds) when you created the workflow action.

`updated_at`

datetime (TIMESTAMP\_MILLIS)

✅

The date-time (in milliseconds) when a workflow action was last updated.

`version`

integer (INT64)

✅

An incrementing number starting at 1 representing the "version" of the workflow action. The largest number for any action represents the latest version. The version changes whenever you update the name, content, or settings of your workflow action.

<!-- /tab -->

<!-- tab: Objects -->

#### Objects

The first Object export file includes a list of current objects at the time of your first sync (deleted objects are not included in the first file). Subsequent exports include objects who were created, deleted, or suppressed since the last export.

**When you enable the *Objects* export, we also export *Object Types*.**

Object exports come in two different files (`v<x>` is your configured schema version—for example, `v9`):

*   `objects_v<x>_<env>_<seq>.parquet`: Contains new objects.
*   `objects_chgs_v<x>_<env>_<seq>.parquet`: Contains changes to objects since the previous sync.

These files have an identical structure and a part of the same data set. You should import them to the same table.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the object.

`object_type_id` foreign key: Object Types

integer (INT32)

✅

Object type IDs begin at 1 and increase sequentially. For example, if you created objects call Accounts and Companies, in that order, they'd have object types `1` and `2` respectively.

`object_id`

string (UTF8)

✅

The ID of the object in question. This will match the ID you see in the Customer.io UI.

`internal_object_id` primary key

string (UTF8)

✅

A unique, immutable ID that Customer.io assigns to the object. Other exports use this value in to reference your object; you can use this export to resolve internal IDs to your object IDs.

`deleted`

boolean

✅

This indicates whether the object has been deleted.

`created_at`

datetime (TIMESTAMP\_MILLIS)

✅

The date/time when the object was added to your workspace.

`updated_at`

datetime (TIMESTAMP\_MILLIS)

✅

The date-time when a object was updated. Use the most recent updated\_at value for an object\_id to disambiguate between multiple records.

<!-- /tab -->

<!-- tab: Object Types -->

#### Object Types

**We export object types when you enable the *Objects* export.** All objects have a *type* indicating what kind of entity they are—like an *account* or *company*. The `object_type` value is an integer starting at 1. For example, if you create two types of objects in your system, accounts and companies, in that order, accounts have an `object_type` of `1` and companies have an `object_type` of `2`.

The first export includes a list of object types at the time of your first sync (we don’t include deleted types in the first file). Subsequent exports include types you created, updated, or deleted since the last sync.

Object type exports come in two different files (`v<x>` is your configured schema version—for example, `v9`):

*   `object_types_v<x>_<env>_<seq>.parquet`: Contains new object types.
*   `object_types_chgs_v<x>_<env>_<seq>.parquet`: Contains changes to object types since the previous sync.

These files have an identical structure and a part of the same data set. You should import them to the same table.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the object.

`object_type_id` primary key

integer (INT32)

✅

Object type IDs begin at 1 and increase sequentially. For example, if you created objects call Accounts and Companies, in that order, they'd have object types `1` and `2` respectively.

`name`

string (UTF8)

✅

The name of the object type, like "Accounts" or "Companies."

`slug`

string (UTF8)

✅

The value you use to reference objects of this type with Liquid. For example, if your object type is *Accounts*, you'll typically reference objects using `{{objects.accounts}}`.

`deleted`

boolean

✅

If true, the object type has been deleted.

`enabled`

boolean

✅

If true, the object type is enabled. You can't use disabled object types in segments, messages, and so on. [Learn more](https://docs.customer.io/journeys/objects-data/objects/types/#enabledisable-an-object-type)

<!-- /tab -->

<!-- tab: Object Attributes -->

#### Object Attributes

Object attribute exports contain changes to object [attribute](https://docs.customer.io/journeys/people/manage/attributes/) . The initial export includes a list of your current objects and their attributes. Subsequent files contain changes to object attributes, with one change per row.

If your object attributes contain nested JSON, the `attribute_name` is the top-level attribute and the `attribute_value` returns the stringified JSON for that attribute.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the person.

`object_type_id` foreign key: Object Types

integer (INT32)

✅

The type of the object represented by the `internal_object_id`. Object type IDs begin at 1 and increase sequentially. For example, if you created objects call Accounts and Companies, in that order, they'd have object types `1` and `2` respectively.

`internal_object_id` primary key foreign key: Objects

string (UTF8)

✅

A unique, immutable ID that Customer.io assigns to the object. You can resolve this value to the object name or ID you're familiar with from the associated *Objects* export.

`attribute_name`

string (UTF8)

✅

The attribute that changed.

`attribute_value`

string (UTF8)

✅

The new value of the attribute.

`timestamp`

datetime (TIMESTAMP\_MILLIS)

✅

The timestamp of the attribute update.

<!-- /tab -->

<!-- tab: Events -->

#### Events

Events are the things people do in your app, on your website, etc. The Events export includes a list of events, with one event per row. Each event includes an `internal_customer_id` that you can use in conjunction with the People table to resolve a person’s `customer_id` or email address.

**The initial sync includes up to 30-days of past events.** Subsequent files contain events since the previous sync interval. We cannot backfill events older than 30 days.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the person.

`event_id` primary key

string (UTF8)

✅

The ID of the event, which may be useful if you need to dedupe events.

`internal_customer_id` foreign key: People

string (UTF8)

✅

The `cio_id` of the person who performed the event. Use the `people` parquet file to resolve this ID to an external `customer_id` or email address.

`name`

string (UTF8)

✅

The event name.

`type`

string (UTF8)

✅

One of `event`, `page`, or `screen`; `page` and `screen` represent page and screenviews respectively. The `event` value represents any other kind of event.

`data`

string (UTF8)

✅

A stringified object containing the event `properties`—the event payload aside from the name, timestamps, and ID.

`timestamp`

datetime (TIMESTAMP\_MILLIS)

✅

The Unix timestamp associated with the event. If you don't set this value yourself, this is the date-time when Customer.io received the event.

`processed_at`

datetime (TIMESTAMP\_MILLIS)

✅

The Unix time when Customer.io processed the event.

`sources`

array (LIST)

✅

The source(s) of the event, e.g. `Customer.io Data Pipelines via JavaScript`.

`source_uas`

array (LIST)

✅

The user agent source(s) of the event, e.g. `Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:132.0) Gecko/20100101 Firefox/132.0`.

<!-- /tab -->

<!-- tab: Inbound -->

#### Inbound

You’ll only see the option to enable this schema if you send SMS through Customer.io.

When someone replies to an SMS message you sent, we record an `inbound` event. The “inbound” export contains one row for each inbound SMS message you receive between syncs. Each event includes an `internal_customer_id` that you can use in conjunction with the People table to resolve a person’s `customer_id` or email address.

**The initial sync includes up to 30-days of past inbound events.** Subsequent files contain events since the previous sync interval. We cannot backfill events older than 30 days.

Field name

Type

Required

Description

`workspace_id`

integer (INT32)

✅

The ID of the Customer.io workspace associated with the inbound message.

`event_id` primary key

string (UTF8)

✅

The unique event identifier, which may be useful if you need to dedupe events.

`internal_customer_id` foreign key: People

string (UTF8)

✅

The `cio_id` of the person who sent the message. Use the `people` parquet file to resolve this ID to an external `customer_id` or email address.

`timestamp`

datetime (TIMESTAMP\_MILLIS)

✅

The Unix timestamp when the person sent the inbound message.

`processed_at`

datetime (TIMESTAMP\_MILLIS)

✅

The Unix timestamp when Customer.io processed the event.

`channel`

string (UTF8)

✅

The messaging channel (e.g., "sms").

`from`

string (UTF8)

✅

The phone number the person sent the inbound message from.

`to`

string (UTF8)

✅

The phone number the person replied to.

`body`

string (UTF8)

✅

The content of the inbound message.

`keyword`

string (UTF8)

✅

The keyword detected in the message, if any.

`optout`

boolean

✅

If true, the message was an opt-out request; if false, it was not.

`messaging_service_sid`

string (UTF8)

✅

The messaging service identifier from the SMS provider.

`message_sid`

string (UTF8)

✅

The unique message identifier from the SMS provider.

`in_reply_to_delivery_id` foreign key: Deliveries

string (UTF8)

✅

The delivery ID of the message this inbound message is replying to, if available. We match inbound messages to deliveries within 72 hours of the original delivery. If the inbound message occurs outside the 72 hour window, or we can't attribute the inbound message to a delivery, this field is `null`.

`message_type`

string (UTF8)

✅

The type of the inbound message.

`has_media`

boolean

✅

Whether the inbound message includes media, such as an MMS attachment.

<!-- /tab -->

<!-- /tabs -->
