These endpoints control reverse ETL: querying your warehouse or lakehouse and writing rows into SaaS tools, ads platforms, and custom HTTP destinations. Authenticate with a Bearer API key.
Base URL: https://api.planasonix.com
Authorization: Bearer YOUR_API_KEY
Content-Type: application/json
If your workspace uses a versioned base path (for example /v1), prepend it before /api. See API reference.
List syncs
Returns all reverse ETL sync definitions.
GET /api/reverse-etl/syncs
Request parameters
Filter syncs targeting a given destination connection.
Filter by active/paused syncs.
Example response
{
"data": [
{
"id": "retl_sync_01j9k6m7n8o9p0q1",
"type": "reverse_etl_sync",
"attributes": {
"name": "crm_accounts_warehouse_to_salesforce",
"destination_id": "conn_sf_prod",
"object_name": "Account",
"sync_mode": "upsert",
"enabled": true,
"schedule": {
"type": "cron",
"expression": "15 */4 * * *",
"timezone": "UTC"
},
"updated_at": "2025-03-26T09:00:00Z"
}
}
],
"meta": {
"page": { "limit": 50, "cursor": null }
}
}
Create sync
Creates a new sync from a SQL or semantic query to a destination object.
POST /api/reverse-etl/syncs
Request body
Display name for the sync.
Warehouse query (SQL) or documented semantic reference that resolves to a row set.
Connection ID for the destination (Salesforce, HubSpot, Iterable, and so on).
Destination object or resource name (API name for CRM entities, stream name for events APIs).
Array of mapping objects: source column or expression → destination field (see example).
One of upsert, insert, update, delete, or connector-specific modes.
Optional schedule: for example { "type": "cron", "expression": "0 * * * *", "timezone": "UTC" } or { "type": "manual" }.
Example response
{
"data": {
"id": "retl_sync_01j9k6m7n8o9p0q1",
"type": "reverse_etl_sync",
"attributes": {
"name": "crm_accounts_warehouse_to_salesforce",
"source_query": "SELECT account_id, name, annual_revenue, last_activity_at FROM analytics.dim_crm_account WHERE is_active = true",
"destination_id": "conn_sf_prod",
"object_name": "Account",
"field_mappings": [
{ "source": "account_id", "destination": "External_Id__c", "key": true },
{ "source": "name", "destination": "Name" },
{ "source": "annual_revenue", "destination": "AnnualRevenue" },
{ "source": "last_activity_at", "destination": "Last_Activity__c" }
],
"sync_mode": "upsert",
"enabled": true,
"schedule": {
"type": "cron",
"expression": "15 */4 * * *",
"timezone": "UTC"
},
"created_at": "2025-03-27T17:00:00Z",
"updated_at": "2025-03-27T17:00:00Z"
}
}
}
Get sync
GET /api/reverse-etl/syncs/{id}
Request parameters
Example response
{
"data": {
"id": "retl_sync_01j9k6m7n8o9p0q1",
"type": "reverse_etl_sync",
"attributes": {
"name": "crm_accounts_warehouse_to_salesforce",
"source_query": "SELECT account_id, name, annual_revenue, last_activity_at FROM analytics.dim_crm_account WHERE is_active = true",
"destination_id": "conn_sf_prod",
"object_name": "Account",
"field_mappings": [
{ "source": "account_id", "destination": "External_Id__c", "key": true },
{ "source": "name", "destination": "Name" }
],
"sync_mode": "upsert",
"enabled": true,
"schedule": {
"type": "cron",
"expression": "15 */4 * * *",
"timezone": "UTC"
},
"created_at": "2025-03-27T17:00:00Z",
"updated_at": "2025-03-27T17:00:00Z"
}
}
}
Update sync
PUT /api/reverse-etl/syncs/{id}
Request parameters
Request body
Same fields as create, all optional except where the server requires a full replacement for certain connectors.
Example response
{
"data": {
"id": "retl_sync_01j9k6m7n8o9p0q1",
"type": "reverse_etl_sync",
"attributes": {
"name": "crm_accounts_warehouse_to_salesforce",
"sync_mode": "upsert",
"enabled": true,
"schedule": {
"type": "cron",
"expression": "0 */2 * * *",
"timezone": "UTC"
},
"updated_at": "2025-03-27T17:15:00Z"
}
}
}
Delete sync
DELETE /api/reverse-etl/syncs/{id}
Request parameters
Example response
{
"data": {
"id": "retl_sync_01j9k6m7n8o9p0q1",
"type": "reverse_etl_sync",
"attributes": {
"deleted": true,
"deleted_at": "2025-03-27T17:20:00Z"
}
}
}
Trigger sync run
Starts a run for one or more syncs (batch semantics depend on your deployment).
POST /api/reverse-etl/sync/run
Request body
Run multiple syncs in one request.
When supported, ignores incremental watermark and reprocesses the full query result set.
Example response
{
"data": {
"id": "retl_run_01j9k7p2q3r4s5t6",
"type": "reverse_etl_run",
"attributes": {
"sync_id": "retl_sync_01j9k6m7n8o9p0q1",
"status": "queued",
"full_refresh": false,
"created_at": "2025-03-27T17:22:00Z"
}
}
}
Toggle sync
Enables or disables a sync without deleting its definition.
POST /api/reverse-etl/sync/toggle
Request body
Example response
{
"data": {
"id": "retl_sync_01j9k6m7n8o9p0q1",
"type": "reverse_etl_sync",
"attributes": {
"enabled": false,
"updated_at": "2025-03-27T17:23:30Z"
}
}
}
List destinations
Lists destination connections available for reverse ETL in the workspace.
GET /api/reverse-etl/destinations
Request parameters
Filter by connector type (for example salesforce, hubspot).
Example response
{
"data": [
{
"id": "conn_sf_prod",
"type": "reverse_etl_destination",
"attributes": {
"name": "Salesforce Production",
"connector": "salesforce",
"status": "connected",
"objects_discoverable": true
}
}
]
}
List destination objects
Returns objects (entities, streams) you can target for a given destination.
GET /api/reverse-etl/objects
Request parameters
Destination connection ID.
Case-insensitive filter on object label or API name.
Example response
{
"data": [
{
"id": "Account",
"type": "destination_object",
"attributes": {
"label": "Account",
"api_name": "Account",
"supports_upsert": true,
"writable": true
}
},
{
"id": "Contact",
"type": "destination_object",
"attributes": {
"label": "Contact",
"api_name": "Contact",
"supports_upsert": true,
"writable": true
}
}
]
}
List object fields
Returns fields for a destination object, including types and write constraints.
GET /api/reverse-etl/fields
Request parameters
Destination connection ID.
Example response
{
"data": [
{
"id": "Name",
"type": "destination_field",
"attributes": {
"label": "Account Name",
"data_type": "string",
"required": true,
"updateable": true,
"external_id": false
}
},
{
"id": "External_Id__c",
"type": "destination_field",
"attributes": {
"label": "Warehouse Account Id",
"data_type": "string",
"required": false,
"updateable": true,
"external_id": true
}
}
]
}
List sync runs
Returns historical reverse ETL run records across syncs.
GET /api/reverse-etl/runs
Request parameters
Example response
{
"data": [
{
"id": "retl_run_01j9k7p2q3r4s5t6",
"type": "reverse_etl_run",
"attributes": {
"sync_id": "retl_sync_01j9k6m7n8o9p0q1",
"status": "succeeded",
"rows_read": 12840,
"rows_written": 12795,
"rows_failed": 45,
"started_at": "2025-03-27T12:15:01Z",
"finished_at": "2025-03-27T12:18:44Z"
}
}
],
"meta": {
"page": { "limit": 50, "cursor": null }
}
}
List dead letter queue entries
Returns rows or batches that failed after retries and require manual review.
GET /api/reverse-etl/dlq
Request parameters
false (default) for open failures; true for history.
Example response
{
"data": [
{
"id": "retl_dlq_01j9k8u9v0w1x2y3",
"type": "reverse_etl_dlq_entry",
"attributes": {
"sync_id": "retl_sync_01j9k6m7n8o9p0q1",
"run_id": "retl_run_01j9k7p2q3r4s5t6",
"error_code": "DESTINATION_VALIDATION",
"message": "INVALID_EMAIL: bad format for field Email",
"record_keys": { "External_Id__c": "acc_88291" },
"attempts": 5,
"first_seen_at": "2025-03-27T12:16:10Z",
"resolved": false
}
}
],
"meta": {
"page": { "limit": 50, "cursor": null }
}
}
DLQ statistics
Aggregate counts for monitoring and alerting.
GET /api/reverse-etl/dlq/stats
Request parameters
Scope stats to one sync; omit for workspace totals.
ISO 8601 timestamp; only count entries at or after this time.
Example response
{
"data": {
"type": "reverse_etl_dlq_stats",
"attributes": {
"open_count": 12,
"resolved_count": 340,
"by_error_code": {
"DESTINATION_VALIDATION": 7,
"RATE_LIMIT": 3,
"UNKNOWN": 2
},
"computed_at": "2025-03-27T17:30:00Z"
}
}
}
Resolve DLQ entries
Marks entries resolved after you fix data or choose to skip them; optional replay may enqueue new writes.
POST /api/reverse-etl/dlq/resolve
Request body
DLQ entry IDs to resolve.
Short reason code: fixed_upstream, skipped, manual_push, and so on.
When true and supported, re-attempts the affected records in a new run.
Example response
{
"data": {
"type": "reverse_etl_dlq_resolve",
"attributes": {
"resolved_ids": ["retl_dlq_01j9k8u9v0w1x2y3"],
"replay_run_id": null,
"resolved_at": "2025-03-27T17:35:00Z"
}
}
}