---
title: "Create warehouse sync"
method: POST
path: "/warehouse-syncs"
tags: ["Warehouse Sync"]
---

# Create warehouse sync

`POST /warehouse-syncs`

## Request

- Base URL: `https://api.sequenzy.com/api/v1`
- URL: `https://api.sequenzy.com/api/v1/warehouse-syncs`
- Auth: HTTP bearer

Creates a sync that runs a query on a schedule and sends changed rows to Sequenzy: contacts (kind subscribers) through the import pipeline, or events on existing contacts (kind events). The mapping and cursor are checked against the query's real columns and the first run starts right away. Requires warehouse:write and subscribers:write; event syncs also need events:write, and triggerAutomations or double_opt_in need automations:trigger.

## Request body

- object
  - `connectionId` string, required
  - `name` string, required
  - `kind` 'subscribers' | 'events', required
  - `query` string, required: A single SELECT or WITH statement.
  - `mapping` object, required: Subscriber syncs: emailColumn (or phoneColumn), externalIdColumn, firstNameColumn, lastNameColumn, tagsColumn, attributes. Event syncs: emailColumn or externalIdColumn, eventNameColumn or eventName, eventIdColumn, occurredAtColumn, properties. Every column must exist in the query.
    - `emailColumn` string, nullable
    - `phoneColumn` string, nullable
    - `externalIdColumn` string, nullable
    - `firstNameColumn` string, nullable
    - `lastNameColumn` string, nullable
    - `tagsColumn` string, nullable: Array column or a comma-separated string.
    - `attributes` object[]
      - `column` string, required
      - `attribute` string, required
    - `eventNameColumn` string, nullable
    - `eventName` string, nullable: Fixed event name when no event name column is mapped.
    - `eventIdColumn` string, nullable: Stable ID per event. Derived from the row when omitted.
    - `occurredAtColumn` string, nullable: Timestamp column. Defaults to the sync time.
    - `properties` object[]
      - `column` string, required
      - `property` string, required
  - `cursorColumn` string, nullable: Column read incrementally, for example updated_at. Each run reads rows at or after the last value it saw.
  - `frequency` 'every_15_minutes' | 'hourly' | 'daily' | 'weekly' | 'manual'
  - `listIds` string[]: Subscriber syncs only. Lists new contacts join.
  - `optInMode` 'confirmed' | 'double_opt_in': Subscriber syncs only. double_opt_in needs automations:trigger.
  - `triggerAutomations` boolean: Let new contacts, or events from the last hour, start automations. Needs automations:trigger.

## Response `201`

Sync created and first run queued

- object
  - `success` boolean
  - `sync` WarehouseSync
    - `id` string
    - `connectionId` string
    - `name` string
    - `kind` 'subscribers' | 'events'
    - `query` string
    - `mapping` object
    - `cursorColumn` string, nullable
    - `cursorValue` string, nullable: Rows with the cursor column at or after this value are read next.
    - `listIds` string[]
    - `optInMode` 'confirmed' | 'double_opt_in'
    - `triggerAutomations` boolean
    - `frequency` 'every_15_minutes' | 'hourly' | 'daily' | 'weekly' | 'manual'
    - `isEnabled` boolean
    - `fullResyncRequested` boolean
    - `status` 'idle' | 'queued' | 'running' | 'failed'
    - `nextRunAt` string, date-time, nullable
    - `lastRunAt` string, date-time, nullable
    - `lastSuccessAt` string, date-time, nullable
    - `lastError` string, nullable
    - `lastErrorAt` string, date-time, nullable
    - `consecutiveFailures` integer
    - `latestRun` WarehouseSyncRun
      - `id` string
      - `syncId` string
      - `status` 'running' | 'succeeded' | 'failed'
      - `isManual` boolean
      - `isFullResync` boolean
      - `rowsRead` integer
      - `rowsUnchanged` integer: Rows skipped because they did not change since they were last sent.
      - `rowsSynced` integer
      - `rowsSkipped` integer: Rows missing required values, or events for unknown contacts. Read again when the row changes, on a full resync, or on every run without a cursor column.
      - `rowsFailed` integer: Rows Sequenzy could not write this time. The cursor stays at the first of them, so the next run reads them again.
      - `errorSamples` object[]
        - `row` integer
        - `reason` string
      - `subscriberImportIds` string[]
      - `imports` object[]: Progress of the subscriber imports this run queued.
        - `id` string
        - `status` string
        - `totalRows` integer
        - `processedCount` integer
        - `addedCount` integer
        - `updatedCount` integer
        - `skippedCount` integer
        - `failedCount` integer
      - `error` string, nullable
      - `startedAt` string, date-time
      - `finishedAt` string, date-time, nullable
    - `createdAt` string, date-time
    - `updatedAt` string, date-time

## Other responses

- `400`: Validation error, or the warehouse rejected the credentials or query (see code)
- `401`: Unauthorized
- `403`: Missing scope, or the member role cannot manage workspace settings
- `404`: Connection or list not found
- `422`: Request body does not match the schema

## Changes

- **2026-10-16** `db3873ca3d4d`: 1 info
  - endpoint added

[Operation history](https://skmtc.dev/sequenzy/apis/sequenzy-api/changes/warehouse-syncs/post.md)

---

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