Subchapter 19.15
references/automations/schemas-and-scheduling.mdMarkdown20 KBView on GitHub
Save restrictions: Before a create, update or activation, follow the complete .
TL;DR
setVariable.* + identity enrichment) — build it yourself, per step (Automations Graph and Data Model (opens in a new tab) §4).updateSchemaOnChange fields; its outputs grow with its mapping. Fetch them — never invent fields.wix_automations-webhook_trigger, wix_automations-custom_trigger; actions webhooks-action, wix_automations-llm_call, ai_custom_agent_bm-delegate_to_agent. Wire fields: trigger.overrideSchema, appDefinedInfo.overrideOutputSchema.trigger.automationConfigMapping = { startDate, repetitions? { cronExpression, everyNthDay | everyNthWeek | everyNthMonth, endDate? } }. No repetitions = one run. Use the exact site’s IANA time zone from current tool-returned metadata, with Site Properties as fallback (§4). Create and Update support automationConfigMapping; include it in the trigger configuration.trigger.scheduledEventOffset (only for triggers with a futureDate field). “After” = a DELAY step (Automations Delays Variables and Branches (opens in a new tab)).Each step may read only its aggregated schema: the trigger payload (override, or static + dynamic merged) + outputs of its ancestor steps under their namespace + setVariable.* + identity enrichment — never a sibling branch, a descendant or a skipped step. Full recipe, identity-enrichment keys and resolution order: Automations Graph and Data Model (opens in a new tab) §4. Only its paths may appear in a step’s var("…") expressions (Automations Mapping Expressions (opens in a new tab)).
| Marker | Meaning | Call |
|---|---|---|
trigger implementedMethods.getDynamicSchema: true | Payload depends on filter selections (reevaluateDynamicSchema filters) | Get Trigger Dynamic Schema (Automations Trigger Configuration (opens in a new tab) §5) |
action input property updateSchemaOnChange: true | Setting it reveals more inputs | Get Action Dynamic Input Schema (Automations Action Configuration (opens in a new tab) §4) |
| action output depends on mapping (e.g. an email template’s placeholders) | Output fields revealed by configuration | Get Action Dynamic Output Schema (Automations Action Configuration (opens in a new tab) §4) |
A field you need but cannot see is unknown until its controlling selection is made — say which selection reveals it; don’t claim the data doesn’t exist, and don’t invent it. Form answers with no form filter chosen are such fields: use only static fields, or ask which form (Automations Trigger Configuration (opens in a new tab) §5).
Replaces a component’s schema so downstream steps get addressable fields. It exists only for components whose data comes from outside Wix and therefore have no schema of their own.
| Component | Wire field |
|---|---|
Trigger wix_automations-webhook_trigger, wix_automations-custom_trigger (app 139ef4fa-c108-8f9a-c7be-d5f492a2c939) | configuration.trigger.overrideSchema |
Action webhooks-action, wix_automations-llm_call, ai_custom_agent_bm-delegate_to_agent | actions[id].appDefinedInfo.overrideOutputSchema |
NEVER add an override to any other trigger or action — including one with a dynamic output schema (that schema is real; fetch it). If an existing automation already carries an override on another component, leave it untouched.
Generate one only when a downstream step consumes the data (“then use…”, “based on the result…”). Skip it when the component is the last step or its default schema suffices.
Schema requirements (both kinds):
"$schema": "http://json-schema.org/draft-07/schema", root "type": "object", properties, required; standard types only; give each property a title.examples entry per property.overrideSchema replaces the payload schema. An action overrideOutputSchema is merged over the catalog output schema (its properties win on a clash) — catalog output fields stay addressable. Get Automation returns overrides only with fields: ["OVERRIDE_SCHEMA"] — use it on read-back, or the override looks lost.payloadDataSchema.properties.webhookId into the initial override alongside the user’s payload fields, before the first validation.payloadDataSchema.required contains webhookId.title as Webhook ID and a missing examples entry using the same webhook UUID from the trigger configuration; do not replace existing ones.hookId: Automations Trigger Configuration (opens in a new tab) §6.Webhook override example. trigger is the trigger object returned by Get Trigger By App Id And Key
(response.trigger), and
webhookId is the same fresh UUID used in the trigger configuration. Build the override
from its live schema by passing both values to buildWebhookOverride(trigger, webhookId);
do not substitute the generic example below for this webhook case.
function buildWebhookOverride(trigger, webhookId) {
const payloadSchema =
typeof trigger.payloadDataSchema === 'string'
? JSON.parse(trigger.payloadDataSchema)
: trigger.payloadDataSchema;
const webhookProperty = payloadSchema.properties?.webhookId;
if (!webhookProperty)
throw new Error('The trigger schema must expose webhookId');
return {
$schema: 'http://json-schema.org/draft-07/schema',
type: 'object',
properties: {
webhookId: {
...webhookProperty,
title: webhookProperty.title || 'Webhook ID',
examples: webhookProperty.examples?.length
? webhookProperty.examples
: [webhookId],
},
leadName: { type: 'string', title: 'Lead name', examples: ['Alex'] },
score: { type: 'number', title: 'Score', examples: [0.8] },
},
required: [
...(payloadSchema.required?.includes('webhookId') ? ['webhookId'] : []),
'leadName',
'score',
],
};
}This example makes the two supplied payload fields required. Keep webhookId‘s original
required membership and constraints. Use the resulting schema as the webhook trigger’s
overrideSchema before the first Validate; read it back with OVERRIDE_SCHEMA after saving.
Generic action-output or non-webhook payload schema:
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"properties": {
"orderId": {
"type": "string",
"title": "Order ID",
"examples": ["order_12345"]
},
"customerEmail": {
"type": "string",
"title": "Customer Email",
"examples": ["customer@example.com"]
},
"totalAmount": {
"type": "number",
"title": "Total Order Amount",
"examples": [89.97]
}
},
"required": ["orderId", "customerEmail", "totalAmount"]
}Per-action rules:
wix_automations-llm_call): when a property is a decision among known outcomes that a later condition branches on, constrain it with enum (e.g. "location": {"type": "string", "enum": ["inside", "outside"]}) — the condition can only match values it can predict.{{var("…")}} values into the prompt/instructions input. “Analyze the order” without mapping the order fields is incomplete even if it validates.isEligible, discountPercentage, reason, not generic success). Change the schema when the instructions change.Timing lives in configuration.trigger.automationConfigMapping, supported by Create and Update and required by the builder. Read the trigger’s automationConfigSchema from Get Trigger By App Id And Key and follow it if it differs. The scheduled trigger carries no payload data — only timing.
"trigger": {
"appId": "139ef4fa-c108-8f9a-c7be-d5f492a2c939",
"triggerKey": "wix_automations-scheduled_trigger",
"filters": [],
"automationConfigMapping": {
"startDate": "2026-10-05T09:00:00-04:00",
"repetitions": { "cronExpression": "0 9 * * 1", "everyNthWeek": 1,
"endDate": "2026-12-28T09:00:00-05:00" }
}
}startDate (required): YYYY-MM-DDTHH:mm:00±HH:MM — seconds 00, no milliseconds, the site time zone’s offset on that date (Z only if the site is UTC). Its hour:minute is the time of every run; its date seeds weekly/monthly patterns. The builder reads the wall-clock digits as site-local time and ignores the offset, then re-writes the string: NEVER write the UTC instant (…T13:00:00Z for 9:00 New York) — the schedule would move to 13:00.
Time zone: use the IANA zone in current tool-returned site metadata for the exact target site, if supplied; otherwise read Site Properties — GET https://www.wixapis.com/site-properties/v4/properties?fields.paths=timeZone (SDK siteProperties.getSiteProperties({fields: ["timeZone"]}) from @wix/business-tools) → properties.timeZone (IANA, e.g. America/New_York). Convert it to the offset valid on each date (DST changes the offset; compute startDate and endDate separately). If neither source supplies a valid zone, ask the user; never use another site’s zone, a documentation example, memory or a silent UTC default.
A schedule always runs in the site’s time zone. If the user names a different zone (“23:30 Berlin time” on a London site), never write that zone’s offset. When the site zone keeps the same offset from theirs all year (for example, both on the same DST calendar), convert the time to site wall-clock time and say which zone the schedule uses. Otherwise a fixed site-time schedule can’t match their time all year: ask whether to change the site time zone or run at a site time. Date checks in a CODE_CONDITION may still use the user’s zone.
One-time: {"startDate": "…"} only — the absence of repetitions means run once (and no endDate).
Recurring: repetitions with cronExpression + exactly one of everyNthDay (1–99) / everyNthWeek (1–52) / everyNthMonth (1–12) — the stricter of the builder’s input (1–99) and the trigger’s config schema (≤ 365 days, 52 weeks, 12 months), optional endDate inside repetitions (same format and time zone, after startDate). Cron is "MM HH DOM MON DOW" (Sunday = 0); N#K = Kth weekday N of the month; NL = last weekday N.
Builder round-trip (it regenerates the cron from startDate when the panel opens — any mismatch is rewritten):
MM HH = startDate‘s minute and hour, no leading zeros ("0 9 …", not "00 09 …").1,3,5). Monthly day-of-month = startDate‘s day; N#K: N = startDate‘s weekday, K = its week of the month (days 1–7 → #1, 8–14 → #2, …).NL to MM HH L * * and resets an endDate to exactly one year after startDate; a save after opening it persists that. Ask them to re-check both before saving from the builder.| Intent | cronExpression | param |
|---|---|---|
| Every day 8:00 | "0 8 * * *" | everyNthDay: 1 |
| Every 5 days | "0 10 * * *" | everyNthDay: 5 |
| Every 2 weeks, weekday irrelevant | "0 9 * * *" | everyNthDay: 14 |
| Every Thursday 10:00 | "0 10 * * 4" | everyNthWeek: 1 |
| Mon/Wed/Fri 8:00 | "0 8 * * 1,3,5" | everyNthWeek: 1 |
| Every 2 weeks Sun + Wed | "12 17 * * 0,3" | everyNthWeek: 2 |
| Weekdays | "0 9 * * 1,2,3,4,5" | everyNthWeek: 1 |
| 15th of every month | "0 9 15 * *" | everyNthMonth: 1 |
| First Monday monthly | "0 10 * * 1#1" | everyNthMonth: 1 |
| Last Friday quarterly | "0 16 * * 5L" | everyNthMonth: 3 |
Weekly: pure interval → everyNthDay: 7/14/21 (runs on startDate‘s weekday); specific weekday(s) → everyNthWeek + cron DOW.
Monthly (most restrictive): startDate MUST itself be an instance of the pattern (a “15th” schedule starts on a 15th; “first Monday” starts on a first Monday). Only two patterns exist: day-of-month 1–28, or weekday position #1–#4 / L.
Not supported — say so and offer an alternative:
L in day-of-month) — never write it, even though the builder’s panel offers a “last day” option when startDate is a month’s last day: it is not a supported pattern. Offer: a daily schedule + a month-end check (below; the only one that fires on the real last day), day 28 (fires up to 3 days early), a last-weekday position (5L), or the 1st.everyNth* params together, or repetitions with none.Daily + month-end check (for “last day of month” or other calendar rules the schedule can’t express): a daily schedule (everyNthDay: 1) whose root is a CODE_CONDITION (Automations Conditions (opens in a new tab) §5) with dynamicVariableExpressions: [] (the scheduled trigger has no payload) and the site time zone written into the code:
/**
* @param {Payload} payload - unused; this check uses the site calendar.
* @returns {boolean} whether today is the site's last day of the month.
*/
export default function (payload) {
try {
const parts = new Intl.DateTimeFormat('en-US', {
timeZone: '<SITE_TIME_ZONE>', // replace with the validated site timeZone
calendar: 'gregory',
numberingSystem: 'latn',
year: 'numeric',
month: 'numeric',
day: 'numeric',
}).formatToParts(new Date());
const get = type => Number(parts.find(part => part.type === type)?.value);
const year = get('year'),
month = get('month'),
day = get('day');
return day === new Date(Date.UTC(year, month, 0)).getUTCDate();
} catch (e) {
return false;
}
}Replace <SITE_TIME_ZONE> with the site’s validated IANA time zone before saving; do not save the placeholder. Validate the site’s IANA time zone before saving; an invalid or unavailable zone is a blocker, never a reason to use UTC. The runtime guard returns false if date formatting fails. Compare calendar dates as above: adding 24 hours is not “tomorrow” across daylight-saving changes. Keep the JSDoc header and the complete snippet within 1000 characters per Automations Conditions (opens in a new tab) §5; put the real steps on its true branch, [] on the false one. Tell the user the check runs in code.
Checklist: startDate matches the pattern day • cron consistent with the everyNth* param • offsets correct for the site time zone on each date • monthly pattern exists in every month • when in doubt prefer daily/weekly over monthly.
For triggers whose payload has a date-time field with futureDate: true (e.g. session start), run the automation a fixed time before that date:
"trigger": { "...": "...",
"scheduledEventOffset": { "preScheduledEventOffsetExpression": "{{2}}",
"scheduledEventOffsetTimeUnit": "DAYS" } }{{N}}, unit MINUTES | HOURS | DAYS | WEEKS | MONTHS. Omit scheduledEventOffset entirely when there is none.{{var("timeOffset")}}. This is an API capability, but the current builder’s timing panel and canvas label recognize only a numeric expression: a variable will not display as the requested reminder window. Do not silently replace a payload-driven request with a fixed number; explain that editor limitation and ask whether a fixed, editable window is acceptable. Preserve an existing variable expression unchanged during unrelated updates; do not rewrite its timing to make the panel display it.futureDate field cannot take an offset.Site actions (wix_automations-wix_api_integration) are generated through non-public services and can’t be created from public APIs — offer an installed app-defined action, a supported webhook action, or an LLM text action where those meet the requested outcome. Leave an existing one’s inputMapping untouched.