> Portal Navigation:
> 
> - Append `.md` to any URL under `https://dev.wix.com/docs/` to get its markdown version.
> - Pages are either content pages (article or reference text) or menu pages (a list of links to child pages).
> - To get a menu page, truncate any URL to a parent path and append `.md` (e.g. `https://dev.wix.com/docs/sdk.md`, `https://dev.wix.com/docs/sdk/core-modules.md`).
> - Top-level index of all portals: https://dev.wix.com/docs/llms.txt
> - Full concatenated docs: https://dev.wix.com/docs/llms-full.txt

## Resource: Automations Schemas and Scheduling

## Article: Automations Schemas and Scheduling

## Article Link: https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-schemas-and-scheduling.md

## Article Content:

> **Stage scope:** this stage covers only what [Build and Manage Wix Automations](https://dev.wix.com/docs/api-reference/business-management/automations/skills/build-and-manage-wix-automations.md) lists; follow the instruction attached to each guide marked "not yet published". "Stage" 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).

# Schemas & Scheduling — Aggregated Schema, Override Schemas, Scheduled and Future-Date Triggers

**TL;DR**

- Every step can reference only its **aggregated schema** (trigger payload + **ancestor** outputs + `setVariable.*` + identity enrichment) — build it yourself, per step ([Automations Graph and Data Model](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model.md) §4).
- Dynamic schemas: trigger payload grows with its filters; an action's inputs grow with `updateSchemaOnChange` fields; its outputs grow with its mapping. Fetch them — never invent fields.
- Override output schema is allowed ONLY on: triggers `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`.
- Scheduled trigger: `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.
- "N days before the event" = `trigger.scheduledEventOffset` (only for triggers with a `futureDate` field). "After" = a DELAY step ([Automations Delays Variables and Branches](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-delays-variables-and-branches.md)).

---

## 1. The aggregated schema

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](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-graph-and-data-model.md) §4. Only its paths may appear in a step's `var("…")` expressions ([Automations Mapping Expressions](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-mapping-expressions.md)).

## 2. Dynamic schemas — markers

| Marker                                                                   | Meaning                                                                  | Call                                               |
| ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | -------------------------------------------------- |
| trigger `implementedMethods.getDynamicSchema: true`                      | Payload depends on filter selections (`reevaluateDynamicSchema` filters) | Get Trigger Dynamic Schema ([Automations Trigger Configuration](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-trigger-configuration.md) §5)      |
| action input property `updateSchemaOnChange: true`                       | Setting it reveals more inputs                                           | Get Action Dynamic Input Schema ([Automations Action Configuration](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-action-configuration.md) §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](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-action-configuration.md) §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](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-trigger-configuration.md) §5).

## 3. Override output schema

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):

- JSON Schema draft-07: `"$schema": "http://json-schema.org/draft-07/schema"`, root `"type": "object"`, `properties`, `required`; standard types only; give each property a `title`.
- Trigger overrides: one realistic `examples` entry per property.
- A trigger `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. For the webhook trigger keep the `webhookId` property in the override if the validator reports it missing. Webhook wiring (id, filter, config mapping) and the custom trigger's `hookId`: [Automations Trigger Configuration](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-trigger-configuration.md) §6.

```json
{
  "$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:

- **LLM (`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.
- **LLM / custom agent — input grounding**: the override describes the result, not the source data. Put the concrete, schema-verified `{{var("…")}}` values into the prompt/instructions input. "Analyze the order" without mapping the order fields is incomplete even if it validates.
- **Custom agent**: the schema must match the agent's task ("check loyalty eligibility" → `isEligible`, `discountPercentage`, `reason`, not generic `success`). Change the schema when the instructions change.
- **Webhook action**: model the called API's response (data / status / timestamp).

## 4. Scheduled trigger (`wix_automations-scheduled_trigger`, app `139ef4fa-c108-8f9a-c7be-d5f492a2c939`)

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.

```json
"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):

- cron `MM HH` = `startDate`'s minute and hour, no leading zeros (`"0 9 …"`, not `"00 09 …"`).
- Weekly weekdays in ascending order (`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`, …).
- Known builder issues (tell the user): opening the schedule panel can rewrite `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:

- Last **day** of month (`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.
- 29th/30th/31st, several days per month, "second-to-last X".
- More than once a day (hourly/minutes).
- Two `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](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-conditions.md) §5) with `dynamicVariableExpressions: []` (the scheduled trigger has no payload) and the site time zone written into the code:

```js
/**
 * @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](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-conditions.md) §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.

## 5. Future-date offset ("before the event")

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:

```json
"trigger": { "...": "...",
  "scheduledEventOffset": { "preScheduledEventOffsetExpression": "{{2}}",
                            "scheduledEventOffsetTimeUnit": "DAYS" } }
```

- For a **builder-editable** fixed reminder, use a positive integer literal `{{N}}`, unit `MINUTES` | `HOURS` | `DAYS` | `WEEKS` | `MONTHS`. Omit `scheduledEventOffset` entirely when there is none.
- The [public scheduled-events contract](https://dev.wix.com/docs/api-reference/business-management/automations/automations/automations-v2/configure-your-automation.md) also supports payload-driven expressions such as `{{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.
- Offsets are always _before_ the event. "2 days after the booking" → no offset; add a DELAY step ([Automations Delays Variables and Branches](https://dev.wix.com/docs/api-reference/business-management/automations/skills/automations-delays-variables-and-branches.md)).
- A trigger without a `futureDate` field cannot take an offset.

## 6. Custom API ("site") actions

Site actions (`wix_automations-wix_api_integration`) are generated through non-public services and can't be created from public APIs — a limitation (§1 of Automations Feasibility and Planning (topic guide not yet published; consult the [Automations API reference](https://dev.wix.com/docs/api-reference/business-management/automations.md))). Leave an existing one's `inputMapping` untouched.