> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sequenzy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Data Exports

> Stream email events, SMS events, custom events and daily subscriber snapshots to your S3 or Google Cloud Storage bucket, ready for Snowflake, BigQuery, Redshift and more.

# Data Exports

Data exports write your Sequenzy data to a bucket you own, every few minutes, as gzipped JSON lines. Load the files into Snowflake, BigQuery, Redshift, Databricks, Athena or DuckDB and join email engagement with the rest of your product data.

You can set up an export in **Settings → Data Warehouse → Export to bucket**, with the [API](/api-reference/data-exports/create), the [CLI](/concepts/cli) (`sequenzy data-exports create`) or the [MCP server](/concepts/mcp) (`create_data_export`).

<Tip>
  Need events in real time instead of in batches? Use [outbound
  webhooks](/integrations/outbound-webhooks). Data exports are built for
  analytics: complete history, stable files, and no endpoint to keep online.
</Tip>

## Datasets

| Dataset | What it contains | How it is written |
| - | - | - |
| `email_events` | Sends, deliveries, delays, bounces, complaints, opens, clicks, unsubscribes and transport failures | New rows every run |
| `sms_events` | SMS sends, deliveries, failures, clicks, opt-outs and replies | New rows every run |
| `custom_events` | Events you track through the API, SDKs and integrations | New rows every run |
| `subscribers` | Every subscriber with status, tags and custom attributes | One full snapshot per UTC day |

Test sends are never exported. Events imported as history (for example through [Import Events](/api-reference/subscribers/events/import) with timestamps older than an hour) are your own backfilled data and are not streamed back out.

## Supported storage

* **Amazon S3.** Provide the bucket, region and an access key.
* **Google Cloud Storage.** Create an [HMAC key](https://cloud.google.com/storage/docs/authentication/hmackeys) for a service account with the **Storage Object Creator** role on the bucket, and choose `gcs` as the provider.
* **S3-compatible storage** such as Cloudflare R2, MinIO or Backblaze B2. Choose `s3` and set the endpoint, for example `https://<account-id>.r2.cloudflarestorage.com`. The endpoint must use HTTPS and resolve to a public address.

The key only needs permission to write objects under your path prefix. Here is a minimal AWS policy:

```json theme={null}
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "s3:PutObject",
      "Resource": "arn:aws:s3:::acme-exports/sequenzy/*"
    }
  ]
}
```

When you save a destination, Sequenzy writes `<prefix>/_sequenzy/connection-test.json` to confirm access. If that fails, nothing is saved and you see the reason, such as a missing bucket, a wrong region or a denied write. Secrets are stored encrypted and are never shown again.

## File layout

```text theme={null}
<prefix>/email_events/date=2026-09-30/email_events-<fromMs>-<toMs>-0000.jsonl.gz
<prefix>/sms_events/date=2026-09-30/sms_events-<fromMs>-<toMs>-0000.jsonl.gz
<prefix>/custom_events/date=2026-09-30/custom_events-<fromMs>-<toMs>-0000.jsonl.gz
<prefix>/subscribers/date=2026-09-30/subscribers-0000.jsonl.gz
<prefix>/subscribers/date=2026-09-30/_SUCCESS
```

* Each file is gzip-compressed JSON, one object per line, up to 50,000 lines.
* `date=` folders are Hive-style partitions, so warehouses can prune by day.
* Event files cover a window of **recorded time** (`fromMs` exclusive, `toMs` inclusive, in epoch milliseconds). A delayed bounce lands in the window in which Sequenzy recorded it, so nothing is missed behind an earlier window.
* The subscriber snapshot is complete when `_SUCCESS` exists. Load a day's snapshot only after that marker appears.

## Delivery guarantees

* **At least once.** If a run fails partway, the retry rewrites the same window with the same file names. In rare cases, such as a changed file count after a retry, a row can appear twice. Deduplicate on `id`.
* **No gaps.** A window only advances after every file in it is written. Exports trail real time by about two minutes so late writes are included.
* **Retries.** Failed runs retry automatically, backing off from the export frequency up to every 6 hours. The dashboard, API and CLI show the last error until a run succeeds.
* **Ordering.** Rows inside a file are ordered by recorded time. Use `occurred_at` for analysis.

The first export includes events recorded after you create the destination. You can include up to 30 days of earlier events with the **First export** option (`startFrom` in the API). Events recorded more than 7 days after they occurred are not part of the stream.

## Row schemas

Every row has `schema_version` (currently `1`). New fields may be added in the same version; existing fields do not change meaning.

### email\_events

| Field | Description |
| - | - |
| `id` | Event ID. Use it to deduplicate. |
| `event_type` | `sent`, `delivered`, `delivery_delayed`, `bounced`, `complained`, `opened`, `clicked`, `unsubscribed` or `failed` |
| `occurred_at` / `recorded_at` | When it happened, and when Sequenzy recorded it (ISO 8601, UTC) |
| `subscriber_id`, `email` | Recipient |
| `email_send_id` | One ID per message. Joins every event of the same email. |
| `message_type` | `campaign`, `sequence` or `transactional` |
| `campaign_id` / `sequence_step_id` | The campaign, or the sequence step, that sent the email |
| `transactional_email_id`, `email_name` | Transactional template ID and the email's subject or name |
| `ab_test_id`, `ab_test_variant_id` | A/B test attribution |
| `bounce_type`, `bounce_sub_type` | For `bounced`: `Permanent` or `Transient` and the detail |
| `complaint_type` | For `complained` |
| `clicked_url` | For `clicked` |
| `ip_address`, `user_agent`, `country_code` | For opens and clicks, when available |
| `recipient_domain`, `mailbox_provider` | For example `gmail.com` and `gmail` |

### sms\_events

`id`, `event_type` (`sent`, `delivered`, `failed`, `clicked`, `opted_out`, `received`), `occurred_at`, `recorded_at`, `subscriber_id`, `sms_send_id`, `campaign_id`, `sequence_step_id`, `segments`, `credits`, `error_code`, `clicked_url`.

### custom\_events

`id`, `event_name`, `occurred_at`, `recorded_at`, `subscriber_id`, `properties` (object), `campaign_id`, `sequence_step_id`.

### subscribers

`snapshot_date`, `id`, `email`, `external_id`, `first_name`, `last_name`, `phone`, `status`, `sms_status`, `tags` (array), `custom_attributes` (object), `created_at`, `updated_at`.

The snapshot reads subscribers in pages while you keep working, so a contact changed during the export may show either version. Deleted subscribers are absent from the next snapshot.

## Load into your warehouse

<Tabs>
  <Tab title="Snowflake">
    ```sql theme={null}
    CREATE STAGE sequenzy_stage
      URL = 's3://acme-exports/sequenzy/'
      STORAGE_INTEGRATION = my_s3_integration
      FILE_FORMAT = (TYPE = JSON COMPRESSION = GZIP);

    CREATE TABLE email_events (raw VARIANT, file STRING);

    -- Run on a schedule, or wrap in a Snowpipe with AUTO_INGEST = TRUE
    COPY INTO email_events (raw, file)
    FROM (SELECT $1, METADATA$FILENAME FROM @sequenzy_stage/email_events/);

    SELECT raw:event_type::string AS event_type, COUNT(DISTINCT raw:id)
    FROM email_events
    GROUP BY 1;

    ```
  </Tab>

  <Tab title="BigQuery">
    ```sql theme={null}
    CREATE EXTERNAL TABLE sequenzy.email_events_raw
    WITH PARTITION COLUMNS (date DATE)
    OPTIONS (
      format = 'NEWLINE_DELIMITED_JSON',
      compression = 'GZIP',
      uris = ['gs://acme-exports/sequenzy/email_events/*'],
      hive_partition_uri_prefix = 'gs://acme-exports/sequenzy/email_events'
    );

    SELECT event_type, COUNT(DISTINCT id)
    FROM sequenzy.email_events_raw
    WHERE date >= DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY)
    GROUP BY event_type;
    ```
  </Tab>

  <Tab title="Redshift">
    ```sql theme={null}
    CREATE TABLE email_events_staging (
      id VARCHAR(128), event_type VARCHAR(32), occurred_at TIMESTAMPTZ,
      subscriber_id VARCHAR(128), email VARCHAR(255), campaign_id VARCHAR(128),
      clicked_url VARCHAR(2048)
    );

    COPY email_events_staging
    FROM 's3://acme-exports/sequenzy/email_events/date=2026-09-30/'
    IAM_ROLE 'arn:aws:iam::123456789012:role/redshift-s3-read'
    FORMAT AS JSON 'auto ignorecase'
    GZIP TIMEFORMAT 'auto';

    ```
  </Tab>

  <Tab title="DuckDB">
    ```sql theme={null}
    SELECT event_type, COUNT(DISTINCT id)
    FROM read_json_auto('s3://acme-exports/sequenzy/email_events/*/*.jsonl.gz',
                        hive_partitioning = true)
    GROUP BY event_type;
    ```
  </Tab>
</Tabs>

## Manage exports

* **Pause and resume.** Paused exports keep their position and continue where they stopped. Pausing or deleting an export stops a run in progress before its next file.
* **Change datasets.** Datasets you add start from the time you add them.
* **Move or rotate keys.** Changing the bucket, prefix or credentials writes a new test file first. Exports continue from their position in the new location, and a subscriber snapshot in progress starts over there. An export already running stops before its next file and continues with the new settings.
* **Export now.** Runs immediately instead of waiting for the schedule.
* **History.** Each run from the last 14 days shows rows, files, size and any error.
* **Delete.** Stops the export. Files already written stay in your bucket.

Only owners and admins can manage data exports. API keys need `data_exports:read` to view them, and `data_exports:write` plus `subscribers:read` and `analytics:read` to create, change, test or run them, and `data_exports:delete` to delete them.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.