Skip to content
GuideAPIANT.aiv2

Two-way sync

View .md

How an APIANT two-way sync keeps records in two apps matched in both directions, what each app must provide, how loop prevention works, and how to test and reset a sync.

A two-way sync keeps records in two apps matched: a create or update in either app is written to the other, and a delete can be too. APIANT remembers which record in one app corresponds to which record in the other, and ignores the changes it wrote itself so the sync does not bounce the same record back and forth.

Ask for it in plain English: "keep contacts in HubSpot and Pipedrive in sync both ways".

Requirements

A two-way sync can be built only when both apps meet these conditions:

RequirementWhy
Each record has a stable IDThe link between the two records is stored by ID
Each record carries a last-modified timestamp, or a version value that changes on every editLoop prevention compares it to the value the sync last wrote
The app can report changes, by webhook or by a list operation APIANT can pollEach side needs a trigger
The app has create and update actions, and a delete action if deletes should syncThe sync writes through them
The create and update actions return the record's ID and last-modified valueThe sync records both after each write

If an app gives no last-modified value for its records, a two-way sync cannot be built for it. Claude tells you so instead of building one.

The update and delete actions receive the linked record's ID in an input named mapped_item_id. Actions built for ordinary automations usually name their ID input differently, so Claude may need to build sync-ready versions of them, which requires Claude Code. See Build a connector, action or trigger.

How a sync is built

A two-way sync is one automation with two triggers and two sync steps:

text
Trigger A   webhook or polling, on the app A connection
Trigger B   webhook or polling, on the app B connection

Step 1      two-way sync action, bound to the app B connection   (writes A's changes into B)
Step 2      two-way sync action, bound to the app A connection   (writes B's changes into A)

Both directions must stay in one automation, because the links between records and the loop-prevention state are stored per automation. Two separate one-way automations cannot share them.

Each trigger is told where to find the record ID and the last-modified value in the records it receives. Each sync action is given the create and update actions of its target app, and where to find the ID and last-modified value in their results.

Routing is automatic. When trigger A fires, the step bound to app A skips itself, because the change came from app A, and the step bound to app B writes it.

Syncing deletes

If an app reports deletes, through a delete webhook or a polling trigger set up to detect them, a second sync action in delete mode removes the linked record in the other app. A delete for a record that was never synced is skipped.

Custom fields

Triggers and list operations often return only basic fields. If the target app's create or update action needs custom fields, Claude adds a Get action before the sync action to fetch the full record.

How loop prevention works

Each record that enters the sync is labelled with its ID and the connection it came from. For every incoming record, APIANT checks its stored state:

The record isResult
Not seen beforeTreated as new, and created in the other app
Seen, with the same last-modified value the sync recordedIgnored. This is the sync's own write coming back.
Seen, with a different last-modified valueTreated as changed: updated in the other app if it is already linked, otherwise created
Seen, with an earlier sync that did not finishSent through again
Missing a last-modified valueSkipped

After each write, the sync action saves the link on both sides and records the target record's new last-modified value, in one transaction, so the reverse trigger recognises the write as its own.

First run

  • A polling trigger's first poll records what the app currently returns and syncs nothing. Only changes made after the sync starts are synced.
  • A webhook trigger syncs its first delivery.

Existing records that match in both apps are not paired up automatically.

What a sync step outputs

OutputMeaning
The target action's output, plus skipped: falseThe record was created, updated or deleted in the target app
skipped: true, reason: self_syncThe change came from this step's own app. Expected on every run.
skipped: true, reason: never_syncedA delete for a record that was never linked

A record that reaches a sync step without its source label fails the step. That happens when an ordinary trigger, not one set up for two-way sync, feeds the step.

Conflicts

APIANT has no conflict resolution for a pair of records edited in both apps before either change is synced. It does not merge the two edits.

Turning a sync on

When you ask Claude or the assistant to turn on an automation containing two-way sync steps, APIANT checks your plan. If the plan does not include two-way sync, the request is refused with "You need an upgraded subscription to use two-way sync functionality." The pricing page lists two-way sync on every plan. See Plans, usage and billing.

Webhook triggers need their URLs registered in each app, unless the app's trigger registers itself. Ask Claude for the URLs. See Webhook triggers.

Testing and resetting

A test run of a two-way sync is a real sync: it writes to the target app and stores link and loop-prevention state. A second test with an unchanged record from a polling trigger or a real webhook delivery is therefore recognised as already synced, not a repeat of the first test. A test fed a hand-written sample body does not carry the source label the sync step needs, so test with real deliveries.

The gear menu's Processing > Reset… clears an automation's trigger state, unprocessed webhooks, pending automatic retries, and lookup table rows saved with This automation scope. Do not rely on it to clear two-way sync links.

See Testing an automation.

Troubleshooting

SymptomLikely causeWhat to do
Updates create duplicates instead of updatingThe update action does not read mapped_item_id, or the create action's result path for the ID is wrongAsk Claude to check the actions' ID inputs and result paths
A record bounces back and forthThe last-modified path points at a field that does not change on write, or the result timestamp path is wrongAsk Claude to compare the recorded timestamp with the app's value
The sync step fails saying the delivery "produced no usable two_way_sync_identifier"An ordinary trigger, or a hand-written test body, feeds the sync stepAsk Claude to set the trigger up for two-way sync
Nothing syncs after turning onA polling trigger took its baselineChange a record in either app and wait for the next poll
Every run shows self_sync skipsNormal: one of the two steps always skipsNo action needed

Next steps

Last updated September 15, 2026