Skill 18 · Webflow Mcp:interactions
Subchapter 18.6
references/index.mdMarkdown10 KBView on GitHub
Hand-written half of the IX3 authoring contract, for agents building interactions through the Designer Extension or MCP write path.
The generated half is capabilities.generated.md:
trigger and action capability tables, target legality, property allowlists, value
modes. Those are rendered from exported constants and cannot drift. Everything
here lives inside a guard body instead, so it is maintained by hand.
Send only what the Interactions panel can author. The storage schema
(parseIX3DataStrict) is deliberately permissive so legacy and runtime data still
parse; the authoring boundary is designerInvariants.ts. A payload that satisfies
the schema can still be refused.
And a payload that satisfies both can still animate nothing. A [from, to] pair on a
To tween, control: 'reverse' on a timeline that has never played, a scrub whose
action timing.duration is a sliver of its canvasDuration, a wf:class array that
is not one combo chain, a mouse-move trigger with no target — each of those saves,
reads back exactly as sent, and does nothing. They are documented on the trigger and
action pages rather than as [REJECTED], because the write succeeds. When a user
reports that a written interaction does nothing, start at
rejects-index.md → “When there is nothing to decode”.
Read the file for the trigger you are building, plus envelope-and-targets.md.
That pair is enough to author any single-trigger interaction.
| Building | Read |
|---|---|
| Click | trigger-click.md |
| Hover / mouse enter / mouse leave | trigger-hover.md |
| Page load | trigger-load.md |
| Scroll, scroll scrub, parallax | trigger-scroll.md |
| Mouse move, cursor follow | trigger-mouse-move.md |
| Custom JS event | trigger-custom.md |
| Navbar, dropdown, conditions | gated-capabilities.md |
| Variable, Rive, Animate Rive | data_interactions_tool action guide |
| Also relevant | Read |
|---|---|
| Envelope, IDs, scope, targets, filters | envelope-and-targets.md |
Animation properties, values, tt, timing, splitText | actions-and-properties.md |
| Timeline roles, groups, percent canvas | timelines-and-groups.md |
| Reduced motion, breakpoint playback rules | conditional-playback.md |
| Editing an existing interaction | updating-interactions.md |
| Size and count limits | limits-and-budgets.md |
| A write succeeded but the user cannot edit it | panel-traps.md |
| A write succeeded but nothing animates | rejects-index.md → “When there is nothing to decode” |
| Decoding a rejection message | rejects-index.md |
Every rule carries exactly one. They are not interchangeable — each implies different behavior when you hit it.
| Tag | Meaning | What to do |
|---|---|---|
[REQUIRED] | Must be present. | Always send it. |
[OMIT] | The Designer never writes it. | Leave the key out entirely. Do not send a default. |
[REJECTED] | The write path refuses it. Cites the guard and a message fragment. | Never author. The error explains itself. |
[PENDING] | The rejection is written but has not landed on every build yet. The guard is named so it can be verified once it does. No rule currently uses this tag — kept for the next time one is in flight. | Never author. Do not rely on getting an error — the write may succeed silently. |
[GATED] | Refused for every caller. The guards take no flag or session parameter, so no flag turns it on for you. | Do not attempt. Tell the user the capability is unavailable. |
[FLAG] | Availability depends on a feature flag that is not visible in the payload, so the same caller may be accepted or refused as rollout changes. No rule currently uses this tag — advanced eases carried it until the gate reached 100% public and no write-boundary guard was found. | Attempt it and handle the refusal. Do not pre-emptively refuse. |
[PANEL-TRAP] | The API accepts it, but the Interactions panel cannot author, display, edit, or clear the result. | Do not author unsolicited. If explicitly asked, warn that the result will not be editable in the Designer. |
[LEGACY-OK-ON-UPDATE] | Refused on create, but forwarded unchanged when an existing interaction is updated without replacing that field. | On a read-then-write flow, pass the stored value through untouched. Do not “fix” it. |
[SILENTLY-DROPPED] | The key is not declared on a non-strict Zod object, so it is stripped before persist. No error, and get echoes a payload that no longer contains it. | Put the field where it belongs. Never infer from a missing field that the capability is unsupported. |
[PANEL-TRAP] and [SILENTLY-DROPPED] are the two classes no error will ever
teach you. A panel trap succeeds and the damage is only visible to the human
afterwards. A silently-dropped key is worse for an agent specifically: the write
returns success, get_interaction round-trips byte-identically, and the missing
field reads as a missing feature. A dogfood run filed three capability gaps that
way — every one of them was a field written one level too high, and every one was
already documented correctly in this pack. The habit that normally rescues an
agent, escalating to the reference on a rejection, cannot fire when there is no
rejection.
Rules cite names like findScrollTriggerError and TRIGGER_REQUIRES_TARGET_KEYS.
Those are provenance, so a Webflow engineer can verify a rule against the source and
so a rename shows up as a broken citation. You are not expected to look them up,
and they are not part of any payload. Everything you need to author an interaction is
in the prose and the quoted error fragments.
The source of record is the Webflow monorepo at
packages/systems/ix3/schema/agent-pack/. The capability tables there are
generated from exported code constants, and a test fails when they drift.
If you are reading this as a published copy in another repo, do not edit it
here. Local changes are overwritten by the next publish and are not covered by
the drift gate. Webflow engineers: MAINTAINING.md, next to the source, has the
guard-citation audit and the publish command.