// connections

Vendor sync

A sync copies a connected vendor's data into your workspace database and keeps it fresh. Your agents read local tables instead of paging a rate-limited vendor API on every run, join them against their own state, and act only on what changed since they last looked.

backfill, polling and webhooksone sync per vendoryour database, your tables
01// what a sync does

One table per stream, named after the vendor

Turning a sync on backfills the history window you chose, then keeps every table current by polling plus the vendor's own webhooks. Each stream lands in its own table, named after the vendor (heyreach_conversations, calcom_bookings), in the same database your agents already use. A workspace has one sync per vendor, and a vendor's own sub-accounts (HeyReach workspaces and senders, Cal.com event types) are filters on that sync rather than separate syncs. Separate vendors get separate tables; nothing is merged across vendors.

VendorDefault tablesWebhooksStatus
Cal.com4Registered by the sync for youverified
HeyReach6Registered by the sync for youverified
Smartlead8Registered by the sync for youbeta
EmailBison6Registered by the sync for youbeta
Calendly4Registered by the sync for youbeta
lemlist6Registered by the sync for youbeta
RB2B2You paste the sync's URL into the vendor's app; the Sync panel shows the steps (Integrations > Webhook)beta
Apollo7None: the vendor sends no webhooks, so the sync pollsbeta
La Growth Machine5Registered by the sync for youbeta
Expandi5You paste the sync's URL into the vendor's app; the Sync panel shows the steps (LinkedIn Settings > Webhooks)beta
Instantly6Registered by the sync for youbeta

beta vendors

A vendor marked beta is built from the vendor's API documentation and has not yet been proven against a live account. It syncs the same way and shows a Beta badge in the Sync panel; a field the documentation does not show stays in _raw rather than in a typed column.

Cal.com (calcom)

TableWhat it holdsKindDefault
calcom_bookingsEvery booking in the history window plus all upcoming ones, kept current by booking webhooks and a repair poll.entityon
calcom_booking_attendeesOne row per attendee (or seat) of each synced booking.entityon
calcom_event_typesThe bookable event types on the account, re-read every hour.entityon
calcom_schedulesAvailability schedules, re-read daily. Not available on an OAuth connection.entityon
calcom_teamsTeams the account belongs to, re-read daily. Not available on an OAuth connection.entityoff
calcom_eventsOne row per webhook delivery, including events with no other record (meetings started and ended, form submissions, recordings). Kept for 90 days by default.eventoff

HeyReach (heyreach)

TableWhat it holdsKindDefault
heyreach_sendersThe LinkedIn accounts connected to the HeyReach workspace, re-read every hour.entityon
heyreach_campaignsEvery campaign with its status and lead progress, re-read every hour and when a campaign completes.entityon
heyreach_campaign_leadsOne row per lead in each campaign with its connection, message and sequence status, kept current by reply and acceptance webhooks, a 15-minute poll and a daily re-read of running campaigns.entityon
heyreach_conversationsLinkedIn inbox threads of the sender accounts in the history window, kept current by reply and tag webhooks, a 15-minute poll and a daily re-read of the last three days.entityon
heyreach_messagesEvery message of the synced conversations, one row each. Kept for 90 days by default.eventon
heyreach_campaign_daily_statsPer campaign and day: views, likes, follows, messages, InMails, connections and reply rates. The last 7 days are re-read daily because counts land late. Kept for 90 days by default.eventon
heyreach_listsLead and company lists in the workspace, re-read daily.entityoff
heyreach_eventsOne row per webhook delivery: the only record of individual sends, views and likes when those events are switched on. Kept for 90 days by default.eventoff

Smartlead (smartlead)beta

TableWhat it holdsKindDefault
smartlead_clientsThe agency client sub-accounts on the Smartlead account, re-read daily.entityon
smartlead_campaignsEvery campaign with its status and client, re-read every hour. Each pass also brings in the leads of running campaigns, and once a day the email steps and daily stats.entityon
smartlead_sequence_stepsThe email steps of each synced campaign in order, with subject lines and A/B variants, re-read daily.entityon
smartlead_email_accountsThe sending mailboxes with their connection, warmup and daily volume, re-read every hour. SMTP and IMAP passwords are never stored.entityon
smartlead_campaign_leadsOne row per lead in each campaign with its status and category, kept current by reply and category webhooks and an hourly read of running campaigns.entityon
smartlead_lead_activitiesOne row per email sent to a lead and per reply received, across every campaign. Open and click counts ride on the sent rows. Kept for 90 days by default.eventon
smartlead_campaign_daily_statsPer running campaign and day: emails sent, opens, clicks, replies, bounces and unsubscribes. The last 7 days are re-read daily because counts land late. Kept for 90 days by default.eventon
smartlead_lead_categoriesThe global and custom lead categories (Interested, Meeting Request, ...), re-read daily.entityon

EmailBison (emailbison)beta

TableWhat it holdsKindDefault
emailbison_campaignsEvery campaign with its status and lifetime counters, re-read every hour. Once a day the pass also re-reads each campaign's email steps.entityon
emailbison_sequence_stepsThe email steps of each synced campaign, with subjects, waits and A/B variants, re-read daily. A step removed from its campaign is marked deleted.entityon
emailbison_leadsEvery lead in the workspace with its verification status, read by EmailBison's daily change filter and kept current by reply and unsubscribe webhooks.entityon
emailbison_repliesInbox replies in the history window with their text, kept current by reply webhooks, a frequent read of the newest replies and a daily full read.entityon
emailbison_eventsEmailBison's event log for the event types switched to webhook (replies, interest, unsubscribes, bounces and untracked replies by default). EmailBison keeps 10 days, so the backfill covers 10 days. Kept for 90 days by default.eventon
emailbison_sender_emailsThe sending mailboxes with their status, daily limit and lifetime counters, re-read every hour.entityon
emailbison_sent_emailsOne row per campaign email sent, with opens, clicks and replies; the last 2 days are re-read daily as counts move. Off by default: high-volume senders pass row caps fast. Kept for 90 days by default.eventoff

Calendly (calendly)beta

TableWhat it holdsKindDefault
calendly_scheduled_eventsEvery booked meeting that starts inside the history window, plus all upcoming ones, kept current by invitee webhooks and a rolling re-read of recent and upcoming meetings (the last 30 days once a day). Calendly reports no edit times, so a change to an older meeting shows up only when a rescan covers it.entityon
calendly_inviteesOne row per invitee of each synced meeting, with their answers, UTM tracking, cancellation, no-show and reschedule links. Read with their meeting.entityon
calendly_event_typesThe bookable event types, re-read every hour; ones Calendly no longer returns are marked deleted.entityon
calendly_usersThe organization's members and their roles, re-read daily (on a member's token, only that member).entityon

lemlist (lemlist)beta

TableWhat it holdsKindDefault
lemlist_campaignsEvery campaign in the team with its status, re-read every hour; campaigns lemlist no longer returns are marked deleted.entityon
lemlist_sequence_stepsEach campaign's sequence steps (email, LinkedIn and other steps, with conditional branches), re-read daily with the campaigns.entityon
lemlist_campaign_leadsOne row per lead in each campaign with its latest state, read from the campaign export: running and paused campaigns every hour, every campaign nightly, and a campaign as soon as a webhook names it.entityon
lemlist_activitiesEvery campaign activity (sends, opens, clicks, replies, LinkedIn steps, bounces, unsubscribes), one row each. Kept for 90 days by default.eventon
lemlist_unsubscribesUnsubscribed addresses at campaign, team or global scope, re-read daily.entityon
lemlist_campaign_daily_statsPer campaign and day (UTC): messages sent, opens, clicks, replies, bounces and interested leads. Days with no activity are not stored. The last 7 days are re-read daily because counts land late. Kept for 90 days by default.eventon

RB2B (rb2b)beta

TableWhat it holdsKindDefault
rb2b_profilesEvery visitor RB2B identified, with their company and their latest visit. RB2B keeps 90 days on its API, so the sync starts with at most the last 90 days.entityon
rb2b_page_viewsOne row per page view RB2B sends to the webhook (every visit when Send repeat visitor data is on). RB2B's API has no page views, so these exist only from the webhook, and views sent while it was off are lost. Kept for 90 days by default.eventon

Apollo (apollo-io)beta

TableWhat it holdsKindDefault
apollo_io_contactsThe people saved in your Apollo workspace that changed within the history window (every contact, up to 100,000, when history is everything), kept current by reading the newest changes on a schedule.entityon
apollo_io_accountsThe companies saved in your Apollo workspace that changed within the history window (every account, up to 100,000, when history is everything), kept current by reading the newest changes on a schedule.entityon
apollo_io_sequencesEvery sequence with its step count and lifetime engagement counts, re-read every hour.entityon
apollo_io_emailsThe emails your sequences sent, with status, the replied flag and the reply class (Apollo does not expose reply text). Kept for 90 days by default; the last two days are re-read every hour because statuses change after sending.eventon
apollo_io_tasksEvery task in the workspace, re-read daily (Apollo offers no way to read only changed tasks).entityon
apollo_io_dealsEvery deal with its amount, stage and outcome, re-read every hour.entityon
apollo_io_email_accountsThe mailboxes connected to Apollo with their daily sending limit, re-read daily.entityon
apollo_io_callsCalls made with Apollo's dialer, with status and duration. Needs a master API key. Kept for 90 days by default; the last two days are re-read every hour.eventoff
apollo_io_usersThe people on your Apollo team. Needs a master API key. Re-read daily.entityoff

La Growth Machine (lagrowthmachine)beta

TableWhat it holdsKindDefault
lagrowthmachine_identitiesThe senders (a LinkedIn account and its mailboxes) of each synced workspace, re-read daily.entityon
lagrowthmachine_campaignsEvery campaign of each synced workspace with its status and lead count, re-read every hour.entityon
lagrowthmachine_campaign_leadsOne row per lead in each running or paused campaign, with its LinkedIn relation, reply and unsubscribe state and its email and LinkedIn counters, re-read every hour.entityon
lagrowthmachine_conversationsInbox conversations of each synced workspace whose last message falls in the history window, kept current on their last-message time.entityon
lagrowthmachine_messagesThe messages of the synced conversations, one row each (La Growth Machine keeps the latest 25 per conversation). Kept for 90 days by default.eventon

Expandi (expandi)beta

TableWhat it holdsKindDefault
expandi_li_accountsThe LinkedIn accounts (senders) of each synced workspace, re-read every hour.entityon
expandi_campaignsThe campaigns of each synced LinkedIn account with their status, contact count and Expandi stats, re-read every hour.entityon
expandi_messengersOne row per lead of each synced LinkedIn account (Expandi calls it a messenger), with its connection and conversation status, re-read every hour.entityon
expandi_messagesThe LinkedIn messages of the synced leads, read again whenever a lead shows new activity. Kept for 90 days by default.eventon
expandi_eventsOne row per webhook delivery from Expandi, stored whole: the only record of individual visits, invitations, likes and email events, and only from the moment a webhook is set up. Kept for 90 days by default.eventon

Instantly (instantly)beta

TableWhat it holdsKindDefault
instantly_campaignsEvery campaign with its status, sending settings and sending accounts, re-read every hour and when a campaign completes.entityon
instantly_campaign_analyticsInstantly's lifetime counters for every campaign: leads, sends, opens, replies, clicks, bounces, unsubscribes and opportunities. Re-read every hour and when a campaign completes.entityon
instantly_campaign_daily_statsPer campaign and day: sends, leads contacted, opens, replies, clicks and opportunities. The last 7 days are re-read daily because counts land late; a day with no activity is not stored. Kept for 90 days by default.eventon
instantly_leadsEvery lead with its campaign, status, interest and open, reply and click counts. Re-read in full every 6 hours, and re-read when a webhook names it.entityon
instantly_repliesEmails received in the Unibox in the history window, with their text. Kept current by reply webhooks and a frequent read of the newest; once a day the last 3 days are read again as read marks change.entityon
instantly_sent_emailsOne row per email sent, with its campaign, step and lead. Off by default: busy senders pass row caps fast. Kept for 90 days by default.eventoff
instantly_accountsThe sending email accounts with their status, last error, warmup status and score, and daily limit. Re-read every hour and on an account error.entityon
instantly_account_daily_statsPer sending account and day: sends, bounces, opens, replies and clicks. The last 7 days are re-read daily. Off by default. Kept for 90 days by default.eventoff
instantly_eventsOne row per webhook delivery: the only record of single opens and clicks once those events are switched to webhooks. Off by default. Kept for 90 days by default.eventoff
02// turning it on

From the vendor's connection page

  1. 01

    Connect the vendor

    Sync lives on the vendor's page under Connections, in the Sync panel between Connection and Tool permissions. Until the vendor is connected the panel only lists what a sync would copy. Cal.com connected over OAuth syncs by polling only: webhooks, schedules and teams need an API key connection.

  2. 02

    Read the preview

    Before anything is created, the panel shows the projected rows, the new tables and the webhook events a month against your plan's limits, plus any warnings. The preview checks the key with one cheap vendor call, so a key the vendor rejects, or a vendor plan without API access, is refused here, with the reason, and never produces a sync. A plan refusal says to upgrade with the vendor and press Start sync again; a new key would not help.

  3. 03

    Start sync

    The sync registers its own webhooks with the vendor, then each stream backfills its history window. The panel shows progress per table while it backfills and switches to Live when every table is caught up. A workspace without a database gets one on the first sync if the plan includes it.

    RB2B and Expandi have no API for webhooks, so for them the Sync panel shows the sync's webhook URL in a copyable field with numbered steps naming the vendor screen to paste it into, then reports whether events arrive: waiting for the first one, receiving, or gone quiet while the vendor account stays busy. Apollo sends no webhooks at all, so its sync polls.

Customize

Customize opens every setting, and re-runs the preview as you change them so you see the effect on your limits before you save. A save that widens the history window backfills the gap.

SettingDefaultChoices
StreamsThe vendor's default streamsSwitch each stream (one table each) on or off. A stream the connection cannot read says why.
FieldsThe useful columns of each streamAdd or drop typed columns per stream. Everything else stays in _raw.
FreshnessAdaptive, every 15 minutes to an hourAdaptive, a fixed interval (15 minutes, 30 minutes, 1 hour, 3 hours, 6 hours, 1 day), or only when you run it.
HistoryThe last 90 days30, 90, 180 or 365 days, everything, or a start date.
ScopeEverything the key can seeNarrow to the vendor's own sub-accounts, campaigns, senders or event types, picked from what your connection can see.
WebhooksThe vendor's recommended eventsPer event: receive it as a webhook, or pick it up by polling. Shows each event's expected volume and how many deliveries a month it would add.
RetentionEvent tables keep 90 daysA rolling 90 days, or keep everything.
Wake an agentNoneOne deployed agent, woken by every webhook event or only the ones you pick.
03// the tables

Read them, never edit them

Synced tables are ordinary tables: query them with agnt_db_select, agnt_db_execute_sql, the /v1/db routes or a dashboard view. Besides the stream's typed columns, every one carries four engine columns:

ColumnWhat it is
_rawThe vendor's whole object as fetched. Fields that are not typed columns are read from here, for example _raw->>'field'.
_hashA fingerprint of _raw. A row whose content did not change is not rewritten.
_synced_atWhen the sync last wrote the row. How fresh that row is.
_deleted_atSet when the vendor no longer has the record. The row stays so you can see it went; filter _deleted_at is null to skip it.

the sync owns these tables

The sync rewrites its tables on every pass, so a hand edit is overwritten and a table altered or dropped by hand breaks the stream. Keep your own state (decisions, drafts, outcomes) in your own tables and join on the vendor's ids. The Database page marks every synced table with the sync it comes from. A table of the same name that the sync did not create is never touched: that stream waits until the name is free or the stream is off.
04// staying fresh

Polling for correctness, webhooks for speed

  • Polling keeps every table correct on its own. Adaptive freshness checks busy tables every 15 minutes and quiet ones up to hourly, and slow-moving tables such as daily stats refresh daily; a fixed interval or manual-only works too. Sync now runs a pass immediately.
  • Webhook events arrive at a webhook endpoint the sync creates and manages itself, through the same receiver as any inbound webhook, and registers its URL with the vendor for you (for RB2B and Expandi you paste it in yourself). Where the vendor signs its deliveries or echoes a token back, the sync checks it before anything is counted or stored; elsewhere the URL itself is the secret.
  • Every webhook delivery counts toward your plan's webhook events for the period. Past the limit the vendor's deliveries are refused, polling keeps the tables correct, and the sync re-registers any webhook the vendor switched off once the period resets.
  • A webhook can arrive before the matching row is written, so freshness is per table: the Sync panel and agnt_sync_status show when each table last synced and when it runs next.
05// limits

Synced rows are your rows

Synced tables count against the workspace database caps exactly like tables you create, and vendor webhook deliveries count against the plan's webhook events. Event tables (messages, daily stats, booking events) keep a rolling 90 days unless you choose to keep everything; entity tables keep every row. The billing page and the Database page break the rows and webhook events down by sync.

PlanTablesRows, all tablesStorageWebhook events / period
Starter1025,000250 MB25,000
Growth50250,0001 GB150,000
Scale2502,500,00010 GB750,000

Table, row and storage caps apply to serverless databases and are summed across the organization; see Workspace database limits.

06// wake an agent

React to vendor events as they happen

A sync can wake one deployed agent when a vendor event arrives as a webhook: a reply, a cancelled booking. Pick the agent in Customize, with every webhook event or only the ones you choose, or set it with agnt_sync_update and wake_agent. Events you set to polling never arrive as webhooks, so they never wake the agent; events left out of your choice are still stored and synced, they just do not wake it.

the agent can wake first

The agent can wake before the synced row lands. Have it treat the event it was woken with as the truth about what just happened, and read the synced tables for the context around it.
07// pause, turn off, delete

Three ways to stop, three different costs

ActionKeptWhat changes
PauseEverythingNo polling and no webhook work until you resume. Resume picks up where it stopped, with the same webhook URL and woken agent.
Turn off syncTables, rows, cursorsThe vendor webhooks are removed and the sync's webhook URL is retired. Turning it back on continues from where the tables left off, with a new webhook URL and secret, and links the agent it woke to the new URL.
Turn off and delete the tablesNothingThe sync is turned off, then every table it created is dropped after a second confirmation that lists them. Turning it on again backfills from scratch.

Disconnecting the vendor stops the sync and removes its vendor webhooks too; the synced tables are kept, and connecting again then turning the sync back on resumes from them.

08// from an agent

The agnt_sync_* tools

Every workspace MCP server serves the sync family as three tools, agnt_sync_read, agnt_sync_write and agnt_sync_delete, each taking an action; a server set up to list tools per operation lists the eleven per-operation tools below instead. A sync is addressed by provider, the vendor's connection id. The Agnt Wizard has the same tools under the same names, and every result carries a sync_url to the Sync panel.

ToolGroupDefault permissionDoes
agnt_sync_catalogagnt_sync_readactWhat each vendor can sync: streams, tables, columns, how each stream updates, webhook events and how they are set up (automatic, pasted by hand with the steps, or none), filters, defaults, whether it is connected, and whether it is verified or beta.
agnt_sync_listagnt_sync_readactEvery sync in the workspace with its status, tables and freshness.
agnt_sync_statusagnt_sync_readactOne sync in full: settings and config_version, per-table freshness, webhooks with why one could not be set up, the webhook URL to paste for RB2B and Expandi (ingest_url), the agent it wakes, usage against the plan.
agnt_sync_previewagnt_sync_readactWhat turning a sync on or changing it would do: projected rows, new tables and webhook events against the plan. Changes nothing.
agnt_sync_runsagnt_sync_readactRun history: kind, status, who asked, timings, row counts per table, errors.
agnt_sync_changesagnt_sync_readactRows of one synced table changed since a cursor.
agnt_sync_enableagnt_sync_writeaskTurn a sync on, with optional settings and an agent to wake.
agnt_sync_updateagnt_sync_writeaskChange settings, pause (enabled: false) or resume, set or clear the agent it wakes.
agnt_sync_runagnt_sync_writeactRun now: incremental, or a backfill from a start date.
agnt_sync_disableagnt_sync_deletealways askTurn the sync off. Tables kept; webhook URL retired.
agnt_sync_drop_tablesagnt_sync_deletealways askDelete the sync's tables. Requires confirm_destructive: true.

A deployed agent whose config has the workspace database enabled gets agnt_sync_status and agnt_sync_changes, and only those two: turning syncs on, changing and dropping them stays with people, the Agnt Wizard and workspace MCP clients. The agent's database policy applies, so a table allowlist must include the synced tables it reads.

Only what changed: agnt_sync_changes

  • Pass stream (the table name) and since, the next_cursor you stored last time. Omit since to read the table from the start, or pass "now" to tail from now. Keep paging while has_more is true.
  • Each change is an upsert (added or updated) or a delete (the vendor removed the record), with its key; include_rows adds the typed columns.
  • The cursor is opaque and belongs to one table. Rows that share a timestamp are never skipped or repeated across pages, and rows newer than 60 seconds wait for a later call.
  • Rows removed by the 90-day retention or by deleting a table are not reported.
agnt_sync_changes resultjson
{
  "provider": "heyreach",
  "stream": "heyreach_conversations",
  "changes": [
    { "key": { "id": "2-ZTQ3Nz..." }, "op": "upsert", "synced_at": "2026-10-10T14:02:11.482913Z" },
    { "key": { "id": "2-YmE1Mm..." }, "op": "delete", "synced_at": "2026-10-10T14:03:40.120544Z" }
  ],
  "next_cursor": "eyJ2IjoxLCJzIjoiaGV5cmVhY2hfY29udmVyc2F0aW9ucyIs...",
  "has_more": false,
  "freshness": { "state": "live", "last_synced_at": "2026-10-10T14:05:02.311Z", "sync_status": "live" },
  "sync_url": "https://app.superagnt.com/dashboard/<workspace-id>/connections/heyreach#sync"
}

From a skill or your own code

TypeScript skills and data jobs read the same two through the SDK. A deployed agent's runtime routes serve them too, POST /runtime/agents/:id/sync/status and POST /runtime/agents/:id/sync/changes, under that agent's own key and database policy.

TypeScript skilltypescript
import { createAgntdata } from "@agntdata/sdk"

const agnt = createAgntdata()

// Freshness first: is the conversations table current enough to act on?
const sync = await agnt.sync.status("heyreach")

// Then only what changed since the cursor this agent stored last run.
const page = await agnt.sync.changes({
  stream: "heyreach_conversations",
  since: storedCursor,   // omit on the first run to read from the start
  limit: 500,
  includeRows: true,
})
// ...act on page.changes, then store page.next_cursor (keep paging while page.has_more)
runtime routebash
curl -X POST "https://api.superagnt.com/runtime/agents/<deployed-agent-id>/sync/changes" \
  -H "Authorization: Bearer $AGNTDATA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "stream": "heyreach_conversations", "since": "now" }'
09// needs attention

What each reason means

A sync reads Backfilling, Live, Paused or Needs attention. Needs attention always carries one of these reasons, shown in plain words on the Sync panel with the fix, and as attention_reason in agnt_sync_status. Rows already synced stay readable throughout.

ReasonWhat it means
credential_rejectedThe vendor rejected the connection's key. Reconnect it on the connection page; the sync resumes on its own.
connection_removedThe connection was removed. Connect it again, then turn the sync back on.
account_changedThe connection now answers as a different vendor account. Turn the sync off and on again to start fresh; delete the tables too to remove the old account's rows.
database_missingThe workspace database is gone (deleted, or removed after a trial ended). The sync resumes once the workspace has a database again.
workspace_walledThe plan lapsed or the trial ended, so syncing is paused. It resumes when a plan is active.
webhook_limit_reachedOver this period's webhook events. Vendor webhooks are refused and the sync keeps up by polling until the period resets.
row_limit_reachedOver the database row limit. New rows wait; updates to existing rows continue.
table_limit_reachedOver the database table limit. New streams cannot start.
storage_limit_reachedOver the database storage limit.
vendor_rate_limitedThe vendor is slowing the sync down. It tries again on its own.
vendor_errorThe vendor returned an error. Sync now retries.
table_conflictA table with the stream's name already exists and the sync did not create it. Rename your table or switch the stream off.
vendor_plan_requiredThe vendor plan on this account does not include API access. Upgrade it with the vendor.
credential_insufficientThe key cannot read every stream. The streams it cannot read are paused.

Manage syncs at app.superagnt.com under Connections, on each vendor's page.