---
title: "Export Custom Events"
method: POST
path: "/v2/exports/custom-events"
---

# Export Custom Events

`POST /v2/exports/custom-events`

## Request

- Base URL: `https://pub-api.conversion.ai/api`
- URL: `https://pub-api.conversion.ai/api/v2/exports/custom-events`
- Auth: one of:
  - API key in header `X-API-Key`
  - API key in query parameter `api_key`

Bulk export of custom events from a half-open `occurredAt` range, ordered by `occurredAt` ascending. Each event's own payload is returned as a JSON object under `data`. The contact's current email and user ID are resolved per row on a best-effort basis (`contactEmail` and `userId` may be `null` if the contact no longer exists).

Send `occurredAt` on the first call, then pass the opaque cursor returned as `pagination.nextCursor` back as `cursor` on every later call. A `null` cursor means there are no more results. Persist the last non-null cursor between syncs to resume. The range may be arbitrarily long and is walked in bounded segments internally, so an empty page with a non-null cursor means there are more results to come rather than the end of the export.

> **Request access.** This endpoint is not enabled by default, contact the Conversion team to request access. Calls from accounts without it return `403 export_not_enabled`.

## Request body

- object
  - `occurredAt` object: The half-open `occurredAt` range to export, from `start` up to but not including `end`. Required on the first call, and omitted on later calls because the cursor carries the range. Supplying both `occurredAt` and `cursor` returns an `invalid_window` error.
    - `start` string, date-time, required: The start of the range. Events that occurred at exactly this time are included. Omitting it on a call without a `cursor` returns an `invalid_window` error.
    - `end` string, date-time: The end of the range. Events that occurred at exactly this time are excluded, so consecutive ranges can be exported without repeating a boundary event. Omit it to export everything up to the time each page is served, which lets a stored cursor keep returning newly arrived events. Supply it to stop the export at a fixed point, for example to re-export a single historical range. Must be after `start`.
  - `cursor` string: The opaque pagination cursor returned as `pagination.nextCursor` by a previous call. Omit (or pass an empty string) to fetch the first page. Send it on its own, with no `occurredAt`. An invalid or malformed cursor returns an `invalid_cursor` error.
  - `limit` integer: The maximum number of events to return per page. Defaults to 1000. Values are clamped to the range [1, 1000] rather than rejected; values above 1000 return 1000 results.

## Response `200`

A page of custom events retrieved successfully.

- object
  - `data` object
    - `events` object[], required: The current page of exported custom events.
      - `eventId` string, uuid, required: The Conversion ID for this event. Stable primary key for warehouse upserts.
      - `contactId` string, uuid, required: The Conversion contact ID the event belongs to (joins `contacts.id`).
      - `occurredAt` string, date-time, required: When the event occurred. This is the value supplied when the event was tracked, and it is the only timestamp this endpoint filters on.
      - `createdAt` string, date-time, required: When Conversion ingested the event. Compare it with `occurredAt` to identify events that were backdated, for example by a warehouse sync or a CSV import.
      - `eventName` string, required: The name the event was tracked under.
      - `source` 'CONVERSION' | 'PUBLIC_API' | 'CONVERSION_FORM' | 'CSV_UPLOAD' | 'SALESFORCE' | 'SNOWFLAKE' | 'BIGQUERY' | 'META' | 'LINKEDIN' | 'ZOOM' | 'ZOOM_EVENTS' | 'SEQUEL' | 'GOLDCAST' | 'ATTENTIVE', required: The channel the event was ingested through.
      - `contactEmail` string, nullable: The contact's current email address. Best-effort: `null` if the contact no longer exists. This is the value at export time, not the value when the event occurred.
      - `userId` string, nullable: The `userId` the contact is identified by in your own system. Best-effort: `null` if the contact no longer exists or has no `userId`.
      - `clientEventId` string, nullable: The `eventId` supplied when the event was tracked, or `null` if none was sent. Use it to join back to the event in your own system.
      - `data` object, nullable: The event's own payload, exactly as it was tracked, or `null` if none was sent. Keys are lowercased on ingest. Because the keys differ per event name, this is returned as a single JSON object rather than flattened into columns.
  - `pagination` object
    - `nextCursor` string, nullable, required: Opaque cursor for the next page, or `null` when there are no more results. This is the single source of truth for whether more results exist; an empty page can still carry a cursor.

## Other responses

- `400`: The request was malformed or contained invalid data.
- `401`: Authentication failed.
- `403`: The export API is not enabled for this account.
- `500`: An unexpected server error occurred.

---

[API](https://skmtc.dev/conversion/apis/conversion-api.md) - [All operations](https://skmtc.dev/conversion/apis/conversion-api/llms.txt) - [OpenAPI document](https://skmtc.dev/conversion/apis/conversion-api/revisions/8422f815402a?raw)
