Skip to Content
PlugSync documentation preview build
ReferenceConfig V3`steps[]` reference - action DSL

steps[] reference - action DSL

A step is a single instruction inside a flow. Every step has an id, an action, and (depending on the action) a handful of action-specific fields.

See also: flows, connector, simple-vs-federated.

Step skeleton

{ "id": "<unique-within-flow>", "action": "<one of the v3 actions; see below for the closed set>", "when": { /* optional WhenCondition */ } // action-specific fields below }

id must be unique within the flow (enforced by FlowV3._unique_step_ids). The action set is closed: any value outside the list above fails with

Unknown action '<x>'; must be one of ['associate_many', 'create', 'create_many', 'delete', 'delete_many', 'find', 'notify', 'plugin', 'post', 'skip', 'transform', 'update', 'update_many', 'upsert', 'upsert_many']

The 10 baseline actions are available under config version: "3.0". The 5 *_many bulk actions (upsert_many, create_many, update_many, delete_many, associate_many) require version: "3.1" per connector.md.

StepV3 declares model_config = ConfigDict(extra="allow"), which means unknown step-level fields pass validation silently. Treat the per-action tables below as the authoritative list of fields the runtime consumes; typos at the step level will not be caught at draft-save time.

associate is reserved. Per the comment at (internal plugsync tooling - see the source repo) line 77, the associate action name is reserved for a future Plan 6b feature and must not be used in v3 configs. Trying to publish a config that uses it will fail validation with the Unknown action error above.

transform

Reshapes data from one schema into another, typically inbound source into canonical.

FieldRequiredNotes
entityyesThe entity name being transformed.
to_schemayesTarget schema key (usually canonical).
from_schemanoSource schema key. Defaults to inbound payload.

Missing field error: transform step requires entity and to_schema.

Example:

{"id": "to_canonical", "action": "transform", "entity": "contact", "from_schema": "fooshop", "to_schema": "canonical"}

find

Look up an existing target record by criteria.

FieldRequiredNotes
targetyesSchema key to query (e.g., hubspot).
entityyesEntity name.
byyesdict of {property: jsonpath} criteria.

Missing field error: find step requires target, entity, by.

Example:

{"id": "find_company", "action": "find", "target": "hubspot", "entity": "company", "by": {"domain": "$.canonical.domain"}}

upsert, create, update, delete

Four variants of writing to a target. They share required fields and differ in semantics (upsert = create-or-update, create = insert only, update = modify only, delete = remove).

FieldRequiredNotes
targetyesSchema key.
entityyesEntity name.
datanoJSONPath or inline dict of properties.
associationsnoList of association payloads attached to the upserted/created record. Shape is consumer-specific (HubSpot uses {to: {id, type}, types: [...]}). The runtime forwards them verbatim.

Missing field error: <action> step requires target and entity (where <action> is the actual action name, e.g., upsert step requires target and entity).

upsert example:

{"id": "upsert_hubspot", "action": "upsert", "target": "hubspot", "entity": "company", "data": "$.payload"}

update may additionally take update_method ("PUT" or "PATCH") and update_url when the target REST endpoint deviates from the default. These fields are declared on StepV3 but are not enforced by the action validator; they are consumed by the runtime when present.

post

Generic HTTP POST. Used for outbound flows pushing to a target’s REST API.

FieldRequiredNotes
targetyesThe schema key whose credential_name provides auth.
entitynoLogical entity (informational, for logs).
urlyesAbsolute or schema-base-relative URL.
bodynoJSONPath or inline dict.
headersnodict of headers.

Missing field error: post step requires target and url.

Example (HubSpot -> Compass):

{"id": "push_compass", "action": "post", "target": "compass_outbound", "entity": "company", "url": "/api/companies/upsert", "body": "$.payload.object.properties"}

notify

Send a notification (Slack, email, webhook, etc. depending on target type).

FieldRequiredNotes
targetyesNotification target.

Missing field error: notify step requires target.

plugin

Invoke a plugin function. Currently this runs as an in-process Python plugin; migration to AWS Lambda is tracked in issue #190.

FieldRequiredNotes
moduleyesPlugin module name.
functionyesFunction name within the module.

Missing field error: plugin step requires module and function.

upsert_many, create_many, update_many, delete_many (v3.1)

Bulk variants for high-volume ingestion. Process a batch of records through one HubSpot batch API call instead of N single calls.

FieldRequiredNotes
targetyesMust equal "hubspot". Bulk actions are HubSpot-only.
entityyesEntity name.
itemsyesJSONPath expression resolving to a list of records to process.
chunk_sizenoBatch size (1-100). Defaults to HubSpot’s API limit.
outputnoJSONPath into the batch response, used by later steps in the same flow.
reconcilenoWhen true, the runtime emits per-record sync events after the batch call.

Missing field errors:

  • <action> step requires items
  • <action> step requires entity
  • <action> step requires target 'hubspot'

Example:

{"id": "bulk_upsert", "action": "upsert_many", "target": "hubspot", "entity": "contact", "items": "$.canonical.contacts", "chunk_size": 50, "output": "$batch_out", "reconcile": true}

associate_many (v3.1)

Bulk association creation. Attaches multiple records to a target record in one batch call.

FieldRequiredNotes
itemsyesJSONPath resolving to a list of association payloads.

Missing field error: associate_many step requires items.

Example:

{"id": "assoc_deals", "action": "associate_many", "items": "$.canonical.deal_associations"}

skip

No-op. Paired with when to express conditional branches.

{"id": "skip_if_test", "action": "skip", "when": {"equals": {"$.payload.env": "test"}}}

The action validator imposes no required fields on skip; it is only useful in combination with a when condition.

when conditions

The optional when field can carry any of the seven WhenCondition variants from (internal plugsync tooling - see the source repo):

VariantShape
WhenExists{"exists": "<jsonpath>"}
WhenFound{"found": "<step-id>"}
WhenEquals{"equals": {"<jsonpath>": <value>}}
WhenGt{"gt": {"<jsonpath>": <value>}}
WhenLt{"lt": {"<jsonpath>": <value>}}
WhenIn{"in": {"<jsonpath>": [<values...>]}}
WhenNot{"not": <inner WhenCondition>} (recursive)

When a when condition evaluates to false the runtime skips the step and continues with the next one.

Last updated on