Stage scope: this stage covers only what Build and Manage Wix Automations lists; do not perform anything marked "Later stages only", and follow the instruction attached to each guide marked "not yet published". "Stage", "Later stages only" and "not yet published" describe how this skill is being rolled out; never mention them to the user. Explain a limit in product terms: what can't be done through the API here and where the user can do it instead (for example, in the automations builder).
How to prove an automation is correct, saved, and editable in the builder before you tell the user it exists. This file owns the create / update / activate procedures. API shapes: Automations API Catalog.
TL;DR
VALID alone does not prove the
builder can draw, edit and re-save the automation. Status-only deactivation follows §3 and
must not be blocked by unrelated configuration errors.Run the §4 checklist on the complete automation object before every Validate call. Items
tagged [code] are deterministic — a small script beats reasoning (ids, graph, namespaces,
var() inventory, field limits). The rest need you to read schemas.
Semantic review (you, not code) — required for every action before acceptance; full rules in Automations Action Configuration §6:
contactId; copy never proves who receives
it; for emails (later stages only — email is outside stage 3) the audience fields decide (§3 of Automations Email Actions (not yet published; email configuration is outside stage 3)).Request: {automation: <full object>, validationSettings?}. Validate the complete candidate
you intend to save; an unsaved object doesn't need an id. For creation, set origin: "USER",
a real name, and configuration.status: "INACTIVE". For updates, preserve the fetched
origin, settings and status unless a status change was requested. For activation, validate
the candidate with configuration.status: "ACTIVE". Never rewrite an installed automation's
origin or deactivate a live automation merely to validate it.
validationSettings.actionIds validates just the listed actions — useful while configuring node
by node. skipProviderValidations: true skips app-specific checks — acceptable for intermediate
checks only. The final gate is always a full validation with no settings.
Persist the exact validated candidate, without retyping component identities. Immediately before Create/Update, compare trigger appId/triggerKey and each action appId/actionKey with their catalog values and the validated object. For creation, configuration edits or activation, any candidate change requires another full Validate; a success on the old object does not cover a changed ID. Status-only deactivation still follows §3. Compare those identities again on read-back before claiming the requested automation was saved.
Response: status is VALID, VALID_WITH_WARNINGS (only errorSeverity: WARNING), or
INVALID (at least one CRITICAL). Target VALID. Accept VALID_WITH_WARNINGS only when you
understand each warning and tell the user about it.
What the builder does with it. When the user opens the automation, the builder runs the same
full Validate and pins every returned error to its node (trigger or action) — mapping errors
included. A CRITICAL shows as a node error and blocks Activate / Publish until fixed; a
WARNING shows as a node warning. Mapping errors show on the step until the user opens it, then
its form replaces them with field-level marks; other configuration errors show as error/warning
text; a provider error shows as "title: message". So an automation you save with errors opens
broken.
Each triggerValidationErrors[] / actionValidationErrors[] item has errorType
(CONFIGURATION_ERROR | PROVIDER_ERROR), errorSeverity, and one of:
configurationError{errorType, filterFieldKey | fieldKey} — a platform check (tables below);providerConfigurationError{title, message, ctaLabel, ctaUrl} — the owning app's own check
(validateConfiguration; often a missing connection or entity).
Action errors also carry actionId, appId, actionKey.Trigger (configurationError.errorType):
NOT_FOUND, INVALID_TRIGGER_KEY — not in this site's catalog → re-resolve; never guess keys.APP_NOT_INSTALLED, MODERATION_MISMATCH — not available on this site → tell the user; don't
substitute silently.DEPRECATED — being retired → prefer its replacement, or tell the user.INVALID_FILTER_FIELD_KEY — not a filter the trigger defines → use only its filters[].INVALID_FILTER_EXPRESSION → rebuild with the filter shapes in Automations Trigger Configuration §4.MISSING_REQUIRED_FILTER → add it (ask the user for its value if needed).Action (configurationError.errorType, with fieldKey — fix the field it names; never delete a
field to silence an error):
NOT_FOUND, INVALID_ACTION_KEY, APP_NOT_INSTALLED, MODERATION_MISMATCH — not available
on this site → re-resolve; pick another action or tell the user.DEPRECATED — the builder shows "Action will be removed soon" → use its replacement
(§2 of Automations Feasibility and Planning (topic guide not yet published; consult the Automations API reference)).INVALID_MAPPING → remove keys not in the effective schema; re-fetch the dynamic input schema.MAPPING_TYPE_MISMATCH → convert (toString, toNumber), map another field, or build the
object shape (e.g. MONEY).MAPPING_MISSING_REQUIRED_FIELD → map it from the payload or ask the user; never a dummy value.MAPPING_SCHEMA_MISMATCH (enum, uuid, email format…) → a legal enum value / real id.MAPPING_VARIABLE_MISSING_FROM_SCHEMA → fix the path against the aggregated schema, or reorder
so the source is an ancestor.SAMPLE_CODE_RUN_FAILED → fix the code step (guard missing fields — Automations Conditions §5).POST_ACTION_NOT_FOUND → a successor id doesn't exist; fix the graph.Create/Update can also fail with ON_BEFORE_SAVE_ACTION_EXCEPTION: the owning app's save hook (e.g. for Send an email) rejected the step. Treat it like a provider error — read the message, fix the mapping or tell the user; don't retry the same write blindly.
PROVIDER_ERROR usually needs something only the user can do (connect an account, pick a
resource, finish setup). Report its title/message and the ctaUrl, and don't loop on it.
VALID (or understood warnings).origin: "USER", configuration.status: "INACTIVE", no settings.
Capture id. Send Create as its own call, after a separate full Validate call has returned
VALID (or understood warnings) — never in the same script as that Validate or as discovery.
Send it once per candidate: if a Create's outcome is unknown, Query Automations before
resending it (Reporting rules below). A Create that was never sent is not an attempt.fields: ["OVERRIDE_SCHEMA"] (Automations API Catalog §2).
Run the Get as a separate call once Create has returned (not batched into the same script),
and keep its response body as returned: the returned automation is the evidence, so don't
replace it with a summary you assemble from selected fields.<automationId>). Click Activate in the
builder to turn it on: https://manage.wix.com/dashboard/<metaSiteId>/triggers/edit/<automationId>".
That first line MUST name the automation, its status and its ID together; don't move the ID
to a later line, because a reader may see only the first line.
Then say what it does, and ask the user to review it in the builder. If the user asked to
turn it on, activate it first (step 5) and make the first line the active state instead:
"Saved <automationId>): fields: ["OVERRIDE_SCHEMA"] so a complete-object update preserves
trigger/action override schemas. Check origin and settings locks
(§ Update restrictions below) before planning any change.ACTIVE, the update goes live immediately — say so and get confirmation
first (consent rules: § Update restrictions below).revision you read, and origin + settings
exactly as fetched (else INVALID_ORIGIN_TYPE, even with a field mask — Automations API Catalog §2).
On a revision conflict, re-Get, re-apply, re-validate.id — the first override of a preinstalled automation
can have a new id. Read back that id and compare the content of every node you changed (and that untouched node ids
survived). A bumped revision with the old content means the update didn't apply — re-send
with fieldMask: configuration,name and verify again.name or a step's displayName inaccurate (e.g. "…1
day…" after the wait became 2 hours; "Add VIP label" now adding another label), offer new
names; rename only with their OK.{{1440}} MINUTES → {{24}} HOURS;
a webhook trigger filter that is missing or has another id is rebuilt from webhookId; unreachable actions and root ids that
point nowhere are deleted; an empty name becomes "Untitled …". The §4 checklist avoids all of
these, so the user's first save doesn't change your automation.Read or change status only as Automations Activation Status describes; this guide adds no status procedure of its own. Deactivation does not require repairing the configuration.
Use this gate before Create, configuration edits and activation, not status-only deactivation. Creation restrictions ("never add", conservative feature support) do not require rewriting existing builder-supported nodes during an unrelated update. Preserve those nodes and their metadata; use the owning reference's preservation rules and full Validate. A failing item is a blocker, not a warning: some make the canvas spin forever or crash, others are silently rewritten or deleted on the user's first save. Each owning reference has a detailed checklist — this list is the union; run those too for the node types you used.
Graph and ids — Automations Graph and Data Model
rootActionIds entry, and it exists in actions. RATE_LIMIT, if present, is
that root and the only one.postActionIds, truePostActionIds,
falsePostActionIds, SPLIT postActionId) has ≤ 1 id and points to an existing action; every
non-root action has exactly one parent and is reachable from the root; no cycles; no MERGE /
mergeActionId. Converging branches = duplicated downstream steps.actions[k].id === k; ids are fresh uuid v4, unique across actions, SPLIT path ids and
the trigger key, and never collide with <id>true, <id>false, <id>-end.type, only its own type's *Info, [] (not omitted) for an
ending successor list, and a builder-format namespace. Namespaces are unique except that
SET_VARIABLES steps share setVariable; their output keys must be unique across steps.
For every action type, skipActionExpression is "{{true}}" or absent.Trigger — Automations Trigger Configuration §7
appId+triggerKey came from Resolve Triggers on this site.id, verbatim fieldKey, Shape A/B expression whose literal value array
holds the form its filter type allows (Automations Trigger Configuration §4.1: quoted entity ids, quoted static
values, or exactly one bare number/boolean); no filter with an empty filterExpression; ≤ 5 filters; every required
filter present, plus required follow-ups of every set parent. Webhook trigger: exactly its
webhookId shape.scheduledEventOffset match
Automations Schemas and Scheduling.Actions — Automations Action Configuration, Automations Entity and Provider Configuration (topic guide not yet published; consult the Automations API reference), §2 of Automations Feasibility and Planning (topic guide not yet published; consult the Automations API reference)
appId+actionKey came from Resolve Actions on this site; no unsupported
action added — including no new code-variable step (the public Create refuses it).
Later stages only — email is outside stage 3: New email steps have a provider-generated mapping used once and persisted before content edits; no fabricated opaque-widget mapping; unrelated existing ones unchanged
(except the documented site-owner audience replacement).inputMapping: only schema keys (an existing email/widget step's mapping passes through unchanged), types and
enums match, every required field mapped (optional ones per Automations Action Configuration §5.2), entity-selector fields hold ids, formulas
only in dynamicValuesOptions.enabled fields.contacts-create_contact) when the trigger payload already has
an identityType: "contact" field — the builder marks it "can't create a contact" and blocks
activation.Expressions and conditions — Automations Mapping Expressions §9, Automations Conditions §6
var() path exists in that node's aggregated schema
— trigger + ancestor outputs only — with a compatible type.; separators; double-quoted, URI-encoded string literals;
number/boolean fields hold exactly one expression of that type.AND/OR and 1–20 panel-renderable expressions;
not both branches empty; otherwise a CODE_CONDITION.Special steps — Automations Delays Variables and Branches
offsetExpression a {{N}} literal + unit) or absolute
(dueDateExpression). RATE_LIMIT: {{1}}, a trigger identifier allowed by
Automations Delays Variables and Branches §2 (preserve existing supported member/user/UUID identifiers), and a
duration ({{24}} HOURS, never {{1440}} MINUTES) unless the user explicitly wants
"only ever once" (no duration).
SET_VARIABLES: matching keys, every property has a title, keys unique across all variable
steps; preserve existing image-variable metadata per Automations Delays Variables and Branches §4.
SPLIT: 2–10 paths.Object and limits
name 1–100 chars (API allows 500; the builder's name field holds 100),
description ≤ 2000, action displayName 1–60, namespace ≤ 100, SPLIT path name ≤ 100.
Expression limits: condition expression ≤ 5000 chars (a longer one fails the builder's save),
code ≤ 1000, filter expression ≤ 5000, skipActionExpression/delay/rate-limit expressions ≤ 1000.
Step count: ≤ 45 actions of any type — at 45 the builder blocks adding or duplicating steps.
Count before duplicating downstream steps into each branch.origin: "USER", no settings (never hidden or readonly). On update:
origin and settings sent back exactly as fetched; status unchanged unless requested.CRITICAL errors, and you can explain every WARNING.Test Automation runs the automation for real: emails and messages are sent to real recipients, contacts/labels/orders change, webhooks fire. It skips delays and works on inactive automations. It is not a dry run.
payload to match the trigger payload schema, with test data the user approves (e.g.
their own contact), never real customers.activationId: report "started" — not completed or succeeded — and tell
the user to check the run's results in the automation's activity view in the Wix dashboard.
Do not rerun the test to confirm it ran.Before any update: Get Automation, read origin, settings, configuration.status, revision.
Locks apply only to origin APPLICATION / PREINSTALLED (a USER automation has none):
settings.readonly === true: nothing can change; every action counts as
read-only and permanent.settings.triggerSettings absent, or
.disableConfigurationModification === true: no trigger or filter changes. These are internal
fields public responses may omit, so for these origins assume the trigger is locked.settings.actionSettings.readonlyActionIds: can't edit them.settings.actionSettings.permanentActionIds: can't delete them or
change their skipActionExpression.actionSettings.disableConditionAddition /
disableDelayAddition (internal, may be missing) forbid those too; if an Update or the builder
rejects a new condition/delay, report the lock instead of retrying.settings.disableStatusChange: can't activate/deactivate.settings.disableDelete: can't delete the automation.settings back exactly as fetched; never edit it to lift a lock.Explain what's locked and why (installed by Wix or an app), say what IS editable, never work around a lock.
Every update:
status is ACTIVE, Update Automation changes the running automation immediately. Tell the user and confirm before writing. (Or offer: deactivate, edit, re-activate.) If the request already contains that consent ("it's live, change it anyway"), don't ask again — record in your answer that it was a live edit made on their stated consent.revision, origin, settings → read-back; revision conflict → re-Get, re-merge, re-validate): §3 of this guide.var() that read a removed step before reusing its namespace. The single-parent tree rules still apply to any restructuring.var() that read its outputs.Last updated: 22 October 2026