Skip to main content
The sync block turns a connector definition into a two-way task sync. It says how to identify a tracker item, which operations to call, how each task field maps to a tracker field, and how to read the tracker’s webhook. The engine does the rest: it receives signed events, writes tasks, pushes task changes back and records every run. Connecting a tracker that ships with a sync block takes no flow and no code. See Connect a tracker. The platform ships one such definition today, for Shortcut. The examples below use a made-up tracker called Acme Tracker so they apply to any service.

Reference syntax

Inside the block, these forms are available. A sync block holds no secret references. The webhook’s signing secret is stored by name on the connection’s ingress record, not in the definition.

Top-level keys

A deleted item on one side archives its counterpart on the other. Nothing is hard-deleted.

A minimal block

entity

The task’s external_id is the prefix plus the item id. That is how the engine finds the task for an item, and the item for a task. See External ids.

operations

Each value names one of the definition’s own operations.

scope and echo

remote_actor is a JSONPath on the event. connection_actor is a $probe.* or $binding.* value, usually the member id the probe returned.

fields

Each entry is {task, remote, rule, ...parameters}. task is one of the nine synced task fields, each mapped at most once. remote is the tracker’s field name, with no spaces. A parameter the rule does not list is refused. Every rule works in both directions and answers “equal” as well: both sides are compared in a canonical form, and a write is sent only for a field that differs. A field already in step writes nothing.
Parameters that take a map or a list, such as map, exclude or snap_to, accept $binding.* and $probe.* references. The map for role_map usually comes from the status map a person fills in during setup.
The eval rule, which runs JavaScript snippets for a field the rules above cannot express, is available only to definitions that ship with the platform. A definition you store yourself that uses it is refused.

Roles

role_map goes through a role, not a status name. Each Space status has a role such as todo, working or done. The tracker’s states map to roles, and each role maps back to one state, so a Space can rename or add statuses without breaking the sync. A status with no role is never overwritten by an inbound change, unless keep_roleless_unless says otherwise.

inbound

Event normalisation

Normalising turns one event body into the items it concerns. It is pure and writes nothing, and the Connector step’s sync_normalise runs it for your own flows. A change is dropped without an error when its id fails id_pattern, its action is not in actions, its kind is not matched, or it is a comment whose parent is empty or fails the pattern. An event that yields no items is reported as skipped with reason no_items, and an echo as skipped with reason echo.

Comment parents

Some trackers send a comment change with no pointer to the item it belongs to. comment_parent is a list of rules; set exactly one selector in each.

outbound

A push is a hint: the engine re-reads the task before writing, so a burst of events does not send stale values. An event caused by the integration’s own writes is skipped as integration_origin, and an event from another Space as other_space. Both stop a loop between the two sides.

comments

Mirrors comments in both directions. Add operations.comments as well. A comment that is only whitespace is skipped as empty_comment.

Outcomes and reasons

Every sync run ends with {outcome, reason?, task_id?, remote_id?, changed?, unmapped_owners?}. The set of outcomes is closed. no_parent and scale_empty are reserved and not currently reported.

How a run behaves

Applying an item creates the task when none is linked and the item exists, archives the task when the item is gone, and updates it otherwise. A create that loses an external_id race waits briefly, re-reads and applies once. An update that meets a version conflict re-applies once on the version it was given. Pushing creates the tracker item at most once. It searches by external id first, so a redelivery links instead of duplicating, and if linking back fails after a create, the duplicate is removed with operations.delete, or archived if there is none. Deliveries are idempotent: a redelivered event lands on the same run and does nothing. A run that failed with a retryable error is run again on redelivery. Items that touch one tracker object run one at a time, and a comment waits for its parent item.

The webhook address

Each connection has two signed addresses on your gateway. Both take POST /connector/sync/<ingress id>, where the id is in followed by 24 hex characters, and the account is read from the address, never from the request. The body is verified as raw bytes before it is read, and is limited to 4 MiB. You never create these addresses yourself. Connecting a tracker registers the tracker’s webhook at its address, and creates the task subscription pointing at the other. Refused calls are counted on the connection card. Runs, one per item or task event, are kept for 30 days and appear in the card’s Run log.

Where to go next

Connector definitions

The document the sync block belongs to.

Connector step

Normalise, apply and push from your own flow.

Task sync

External ids, versions and the changes feed.

Connect a tracker

The setup wizard and connection card.