ReferenceReference by module

Table sync API

Every Table sync action (7) and data model (2): endpoints, scopes, input, preconditions, events and examples.

Keep a table filled from an outside database, Airtable, a Google Sheet or a CSV link — or in step with your customers, both ways.

Module code data_sync · 7 actions · 2 data models · 8 events. A key or token never does more here than the person behind it can; row-level security applies exactly as in the app. Scopes: data_sync (everything), data_sync:read (read only) or data_sync:<capability>.

Resources

ResourceTableList and readPOSTPATCHDELETE
data_sync_runsdata_sync_run/api/v1/data_sync_runs
data_syncsdata_sync/api/v1/data_syncs

Lists take limit (up to 200), cursor, order (e.g. -created_at), fields and q; every other query parameter is a column filter: ?status=paid, ?total[gte]=100, ?id[in]=a,b, ?x=null.

Actions

ActionNeedsWhat it does
data_sync.create_syncdata_sync:manageSync a table from a source
data_sync.delete_syncdata_sync:manageRemove a sync
data_sync.record_rundata_sync:handleRecord a sync run’s progress
data_sync.request_rundata_sync:manageSync now
data_sync.retry_rundata_sync:manageRetry a failed run
data_sync.set_pauseddata_sync:managePause or resume a sync
data_sync.update_syncdata_sync:manageChange a sync

data_sync.create_sync

Sync a table from a source — Points a table at a source outside the platform and says how its fields map.

Needs data_sync:manage · reaches outside (sends, publishes or charges) · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/data_syncs/actions/create_sync
API key or OAuth token · data_sync:manage
  • Canonical address: POST /api/v1/actions/data_sync.create_sync

Input

FieldTypeRequiredNotes
dataTableIduuidyesTable · id of data_table
titlestringyesName · max 160
sourceKindstring (enum)yesSource · Allowed values: postgres, airtable, csv_url, module, google_sheets
credentialIduuidConnection · id of credential
sourceConfigobject / arrayyesWhere to read
fieldMapobject / arrayyesField mapping
sourceKeystringyesSource key field · max 120
keyFieldstringyesTable key field · max 120
cursorFieldstringCursor field · max 120
onSourceDeletestring (enum)When a source record disappears · Allowed values: keep, delete
scheduleMinutesnumberRun every (minutes) · min 5 · max 10080
twoWaybooleanEdits in the table update the records

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
sync_table_not_foundA sync writes into a table.
invalid_source_configThe source must say where to read, in its own terms.
credential_requiredA database or Airtable source reads with an Apps connection.
key_field_unknownRecords are matched on a stored field of the table.
invalid_field_mapEach source field maps onto one stored field.

Events it publishes: data_sync.sync_created

What it writes: data_sync (insert) — Writes the sync

curl -X POST https://app.getera.io/api/v1/data_syncs/actions/create_sync \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"dataTableId":"DATA_TABLE_ID","title":"Name","sourceKind":"postgres","sourceConfig":{},"fieldMap":{},"sourceKey":"Source key field"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "data_sync.sync_created"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.dataSyncs.createSync({
  dataTableId: "DATA_TABLE_ID",
  title: "Name",
  sourceKind: "postgres",
  sourceConfig: {},
  fieldMap: {},
  sourceKey: "Source key field"
})
era data_syncs create_sync --data '{"dataTableId":"DATA_TABLE_ID","title":"Name","sourceKind":"postgres","sourceConfig":{},"fieldMap":{},"sourceKey":"Source key field"}'
tools/call
{
  "name": "era__data_sync__create_sync",
  "arguments": {
    "dataTableId": "DATA_TABLE_ID",
    "title": "Name",
    "sourceKind": "postgres",
    "sourceConfig": {},
    "fieldMap": {},
    "sourceKey": "Source key field"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold data_sync:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 sync_table_not_found, invalid_source_config, credential_required, key_field_unknown, invalid_field_map · 429 slow down

data_sync.delete_sync

Remove a sync — Stops syncing. The records it wrote stay in the table.

Needs data_sync:manage · removes or cannot be undone · asks a person to confirm when Era runs it

POSThttps://app.getera.io/api/v1/data_syncs/{id}/actions/delete_sync
API key or OAuth token · data_sync:manage
  • Canonical address: POST /api/v1/actions/data_sync.delete_sync

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Sync · id of data_sync

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
sync_not_foundThe sync must exist.
sync_runningA running sync is not removed from under itself.

Events it publishes: data_sync.sync_deleted

What it writes: data_sync (delete) — Removes the sync and its run history

curl -X POST https://app.getera.io/api/v1/data_syncs/DATA_SYNC_ID/actions/delete_sync \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "data_sync.sync_deleted"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.dataSyncs.deleteSync("DATA_SYNC_ID")
era data_syncs delete_sync DATA_SYNC_ID
tools/call
{
  "name": "era__data_sync__delete_sync",
  "arguments": {
    "id": "DATA_SYNC_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold data_sync:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 sync_not_found, sync_running · 429 slow down

data_sync.record_run

Record a sync run’s progress — The sync job’s bookkeeping: counts, the checkpoint and the outcome.

Needs data_sync:handle · changes data

POSThttps://app.getera.io/api/v1/data_sync_runs/{id}/actions/record_run
API key or OAuth token · data_sync:handle
  • Canonical address: POST /api/v1/actions/data_sync.record_run

Input

FieldTypeRequiredNotes
runIduuidyesFilled from the path {id} · Run · id of data_sync_run
statusstring (enum)yesStatus · Allowed values: running, succeeded, failed
fetchednumberRead
insertednumberAdded
updatednumberUpdated
deletednumberDeleted
skippednumberSkipped
cursorAfterstringCheckpoint · max 2000
errorstringError · max 4000
errorsobject / arrayRecord errors

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
run_not_foundThe run must exist.
run_already_finishedA finished run is not written again.

Events it publishes: data_sync.run_succeeded, data_sync.run_failed

What it writes: data_sync_run (update) — Adds the counts and the outcome; data_sync (update) — Moves the checkpoint and the health

curl -X POST https://app.getera.io/api/v1/data_sync_runs/DATA_SYNC_RUN_ID/actions/record_run \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"status":"running"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "data_sync.run_succeeded",
    "data_sync.run_failed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.dataSyncRuns.recordRun("DATA_SYNC_RUN_ID", {
  status: "running"
})
era data_sync_runs record_run DATA_SYNC_RUN_ID \
  --status "running"
tools/call
{
  "name": "era__data_sync__record_run",
  "arguments": {
    "runId": "DATA_SYNC_RUN_ID",
    "status": "running"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold data_sync:handle · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 run_not_found, run_already_finished · 429 slow down

data_sync.request_run

Sync now — Queues a run as a platform job. A full run re-reads everything and can notice deletions.

Needs data_sync:manage · changes data

POSThttps://app.getera.io/api/v1/data_syncs/{id}/actions/request_run
API key or OAuth token · data_sync:manage
  • Canonical address: POST /api/v1/actions/data_sync.request_run

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Sync · id of data_sync
fullRefreshbooleanRe-read everything
triggerstring (enum)Why · Allowed values: manual, schedule

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
sync_not_foundThe sync must exist.
sync_pausedA paused sync does not run.
sync_runningOne run at a time per sync.
credential_requiredThe connection must still be there.

Events it publishes: data_sync.run_requested

What it writes: data_sync_run (insert) — Queues the run; data_sync (update) — Marks the sync queued

curl -X POST https://app.getera.io/api/v1/data_syncs/DATA_SYNC_ID/actions/request_run \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"fullRefresh":true}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "data_sync.run_requested"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.dataSyncs.requestRun("DATA_SYNC_ID", {
  fullRefresh: true
})
era data_syncs request_run DATA_SYNC_ID \
  --full-refresh true
tools/call
{
  "name": "era__data_sync__request_run",
  "arguments": {
    "id": "DATA_SYNC_ID",
    "fullRefresh": true
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold data_sync:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 sync_not_found, sync_paused, sync_running, credential_required · 429 slow down

data_sync.retry_run

Retry a failed run — Queues a new run from the last committed checkpoint.

Needs data_sync:manage · changes data

POSThttps://app.getera.io/api/v1/data_sync_runs/{id}/actions/retry_run
API key or OAuth token · data_sync:manage
  • Canonical address: POST /api/v1/actions/data_sync.retry_run

Input

FieldTypeRequiredNotes
runIduuidyesFilled from the path {id} · Run · id of data_sync_run

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
run_not_foundThe run must exist.
run_not_failedOnly a failed run is retried.
sync_not_foundThe sync must still exist.
sync_pausedA paused sync does not run.
sync_runningOne run at a time per sync.
credential_requiredThe connection must still be there.

Events it publishes: data_sync.run_requested

What it writes: data_sync_run (insert) — Queues the retry; data_sync (update) — Marks the sync queued

curl -X POST https://app.getera.io/api/v1/data_sync_runs/DATA_SYNC_RUN_ID/actions/retry_run \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "data_sync.run_requested"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.dataSyncRuns.retryRun("DATA_SYNC_RUN_ID")
era data_sync_runs retry_run DATA_SYNC_RUN_ID
tools/call
{
  "name": "era__data_sync__retry_run",
  "arguments": {
    "runId": "DATA_SYNC_RUN_ID"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold data_sync:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 run_not_found, run_not_failed, sync_not_found, sync_paused, sync_running, credential_required · 429 slow down

data_sync.set_paused

Pause or resume a sync — A paused sync keeps its checkpoint and its schedule does not fire.

Needs data_sync:manage · changes data

POSThttps://app.getera.io/api/v1/data_syncs/{id}/actions/set_paused
API key or OAuth token · data_sync:manage
  • Canonical address: POST /api/v1/actions/data_sync.set_paused

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Sync · id of data_sync
pausedbooleanyesPaused

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
sync_not_foundThe sync must exist.

Events it publishes: data_sync.sync_paused, data_sync.sync_resumed

What it writes: data_sync (update) — Flips it between paused and idle

curl -X POST https://app.getera.io/api/v1/data_syncs/DATA_SYNC_ID/actions/set_paused \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"paused":true}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "data_sync.sync_paused",
    "data_sync.sync_resumed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.dataSyncs.setPaused("DATA_SYNC_ID", {
  paused: true
})
era data_syncs set_paused DATA_SYNC_ID \
  --paused true
tools/call
{
  "name": "era__data_sync__set_paused",
  "arguments": {
    "id": "DATA_SYNC_ID",
    "paused": true
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold data_sync:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 sync_not_found · 429 slow down

data_sync.update_sync

Change a sync — Changes what it reads or how it maps. A changed key or source starts the next run from the beginning.

Needs data_sync:manage · changes data

POSThttps://app.getera.io/api/v1/data_syncs/{id}/actions/update_sync
API key or OAuth token · data_sync:manage
  • Canonical address: POST /api/v1/actions/data_sync.update_sync

Input

FieldTypeRequiredNotes
iduuidyesFilled from the path {id} · Sync · id of data_sync
titlestringName · max 160
credentialIduuidConnection · id of credential
sourceConfigobject / arrayWhere to read
fieldMapobject / arrayField mapping
sourceKeystringSource key field · max 120
keyFieldstringTable key field · max 120
cursorFieldstringCursor field · max 120
onSourceDeletestring (enum)When a source record disappears · Allowed values: keep, delete
scheduleMinutesnumberRun every (minutes) · min 5 · max 10080
twoWaybooleanEdits in the table update the records

Preconditions

The action is refused with 409 and one of these codes:

CodeRefused when
sync_not_foundThe sync must exist.
invalid_source_configThe source must say where to read, in its own terms.
credential_requiredA database or Airtable source reads with an Apps connection.
key_field_unknownRecords are matched on a stored field of the table.
invalid_field_mapEach source field maps onto one stored field.

Events it publishes: data_sync.sync_changed

What it writes: data_sync (update) — Rewrites the sync

curl -X POST https://app.getera.io/api/v1/data_syncs/DATA_SYNC_ID/actions/update_sync \
  -H "Authorization: Bearer $ERA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: $(uuidgen)" \
  -d '{"title":"Name"}'
Response
{
  "ok": true,
  "result": "…",
  "events": [
    "data_sync.sync_changed"
  ]
}
import { createEra } from "@getera/sdk"

const era = createEra({ apiKey: process.env.ERA_API_KEY })
const res = await era.dataSyncs.updateSync("DATA_SYNC_ID", {
  title: "Name"
})
era data_syncs update_sync DATA_SYNC_ID \
  --title "Name"
tools/call
{
  "name": "era__data_sync__update_sync",
  "arguments": {
    "id": "DATA_SYNC_ID",
    "title": "Name"
  }
}

Errors: 401 no or expired credential · 403 the credential does not hold data_sync:manage · 404 the record or module is outside what it may reach · 422 invalid input (problems[]) · 409 sync_not_found, invalid_source_config, credential_required, key_field_unknown, invalid_field_map · 429 slow down

Data models

DataSyncRun

Table data_sync_run · Resource data_sync_runs

  • GET /api/v1/data_sync_runs
  • GET /api/v1/data_sync_runs/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
sync_idstring (uuid) · uuiddata_sync
statusstring · textqueued, running, succeeded, failed
triggerstring · textmanual, schedule, retry
retry_ofstring (uuid) · uuidyesdata_sync_run
cursor_beforestring · textyes
cursor_afterstring · textyes
fetchedinteger · int4
insertedinteger · int4
updatedinteger · int4
deletedinteger · int4
skippedinteger · int4
errorsobject · jsonb
errorstring · textyes
started_atstring (date-time) · timestamptzyes
finished_atstring (date-time) · timestamptzyes
created_bystring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz

Primary key: id

Checks:

  • CHECK ((jsonb_typeof(errors) = 'array'::text))

DataSync

Table data_sync · Resource data_syncs

  • GET /api/v1/data_syncs
  • GET /api/v1/data_syncs/{id}
ColumnTypeNullableAllowed valuesReferences
idstring (uuid) · uuid
organization_idstring (uuid) · uuidorganization
workspace_idstring (uuid) · uuidworkspace
data_table_idstring (uuid) · uuiddata_table
titlestring · text
source_kindstring · textpostgres, airtable, csv_url, module, google_sheets
source_configobject · jsonb
field_mapobject · jsonb
source_keystring · text
key_fieldstring · text
cursor_fieldstring · textyes
cursor_valuestring · textyes
on_source_deletestring · textkeep, delete
schedule_minutesinteger · int4yes
statusstring · textidle, queued, running, error, paused
last_run_atstring (date-time) · timestamptzyes
last_success_atstring (date-time) · timestamptzyes
last_errorstring · textyes
consecutive_failuresinteger · int4
created_bystring (uuid) · uuidyesapp_user
created_atstring (date-time) · timestamptz
updated_atstring (date-time) · timestamptz
two_wayboolean · bool

Primary key: id

Checks:

  • CHECK (((jsonb_typeof(source_config) = 'object'::text) AND (jsonb_typeof(field_map) = 'object'::text)))
  • CHECK (((schedule_minutes IS NULL) OR ((schedule_minutes >= 5) AND (schedule_minutes <= 10080))))
  • CHECK ((btrim(title) <> ''::text))
  • CHECK (((NOT two_way) OR (source_kind = 'module'::text)))

Events

Workflows and webhooks react to these events.

EventWhat happensPayload
data_sync.run_failedA sync run failed.syncId, runId
data_sync.run_requestedA sync run was queued.syncId, runId, trigger
data_sync.run_succeededA sync run finished.syncId, runId
data_sync.sync_changedA sync’s source or mapping changed.id
data_sync.sync_createdA table was set to sync from a source.id, dataTableId, sourceKind
data_sync.sync_deletedA sync was removed; its rows stayed.id
data_sync.sync_pausedA sync was paused.id
data_sync.sync_resumedA sync was resumed.id
Was this page helpful?