For the complete documentation index, see llms.txt. This page is also available as Markdown.

👣Step catalog

Canonical list of Workflow Foundation steps. How-tos stay short and link here. Screen guides do not copy this page.

In the builder, steps live on Build under Add a step. Each tile adds one node. Click a node to fill its fields in the inspector. Drag to reorder. The builder rewrites next_step_id for a linear path.

A step is three things:

  1. Step — the kind of work (create a row, send email, wait, open a screen).

  2. Config — the fields for that type (data source, recipients, screen, topic, delay).

  3. Place in the list — live-screen steps only update the open tab if they run before background work (Create, Notify, Wait, Publish message). Call webhook and wait, Update record, Change status, and Assign user can stay inline when Live screen updates is on for the app.

Use the smallest step that does the job. Do not add Condition plus Branch when one Condition is enough. Do not use Notify → Webhook when Make must refresh the tab the user already has open — that is Call webhook and wait.

What this is / when to use it

A step is one node in a run. Type enum: WorkflowStepType. Each step has id, name, config, next_step_id, optional on_error_step_id, and optional branches. Successful output is stored as context.<step_id> for later steps.

Before you start

Workflow entitlement is on. The trigger is set. Notion properties you write are writable. Screens, buttons, and Messaging channels you name exist. You have read Which automation and the Trigger catalog.

Build it

  1. Open Automation → Workflows and select the workflow (or start from a wizard).

  2. On Build, click an Add a step tile.

  3. Name the step in plain language (Create follow-up task, not Step 2).

  4. Fill the inspector fields below. Use Event data for payload.* / context.* and Fixed value for literals.

  5. Put live-screen steps (Refresh, Show message, Open screen) before Create / Notify / Wait when the open tab must update.

  6. Save, publish the workflow, and publish the app. Confirm the run on Activity.

Every control

These are the Add a step tiles on Build:

Add a step
Enum
What it does
Main fields
Do not use when

Create record

create_notion_record

Adds a new row to a connected database

Data source, field mappings

You meant to change the row that already triggered the run

Update record

update_notion_record

Changes fields on an existing row

Data source, record path, field mappings

You do not have a record id (payload.record_id or earlier step output)

Create related records

create_related_records

Creates several child rows linked to the trigger record

Related data source, relation field, labels

The related sheet has no relation property pointing at the parent

Notify

send_notification

Email, in-app Notification Center, or fire-and-forget webhook

Channel, recipients or endpoints, subject, body

Make must return JSON and refresh the open tab

Call webhook and wait

call_webhook_and_wait

POSTs Make (or another URL), waits for JSON, then continues on the same tab

Webhook URL, sync timeout, optional headers/body

You only need a fire-and-forget POST — use Notify → Webhook

Open screen

open_screen

Moves the current user to another screen

Screen, presentation, optional record path

The user is not in a live tap (scheduled / webhook-only run)

Show or hide control

set_control_visibility

Shows or hides one control this session

Control, visibility, optional screen

You need a permanent builder change — edit the screen instead

Set control text

set_control_text

Replaces visible text this session

Control, text source, optional screen

You need to write a Notion property — use Update record

Refresh data

refresh_screen_data

Reloads rows on a screen

Optional screen, optional data source

Nothing wrote data yet — put Update / Change status first

Show message

show_message

Short toast on the current screen

Message, tone

You need email — use Notify

Stop action

block_interaction

Stops default button/form follow-through

Optional message

The save already completed and you only wanted a toast — use Show message

Publish message

publish_message

Publishes on Application Messaging; can create a Decision or Work Queue card

Channel, topic, inbox item, routing

You do not have Messaging entitlement

Send request

send_message_request

Publishes a request and parks the run until a reply

Channel, topic, routing, timeout

You only need a one-way announce — use Publish message

Send reply

send_message_reply

Replies to the triggering message or correlation

Topic, reply status, payload

This run was not started by a message / request

Wait for message

wait_for_message

Pauses until a matching topic arrives

Channel, primary topic, wake-on topics, timeout

You only need a clock delay — use Wait

Condition

condition

Checks payload or earlier output before continuing

Rules (field, operator, value)

You need several named paths — that is Branch (templates / API)

Wait

wait

Pauses for a duration or until a time

Delay seconds or Wait until, optional escalation step

The next step must happen in the same tap — put it before Wait

End

end

Marks the path complete

None

Optional; the last step with no next step also finishes

Also used by recipes and templates (not separate Add a step tiles):

Step
Enum
What it does
Main fields

Change status

change_status

Writes a status/select on the current record

Data source, record path, field mappings (status value)

Assign user

assign_user

Writes an assignee / person field

Data source, record path, field mappings (user)

Branch

branch

Multi-way split with labeled conditions

Rules / branch labels

Live screen updates (Workflow Operations → Controls) is off by default. NotionApps turns it on per app. When it is on, Update / Change status / Assign user / Call webhook and wait can Refresh the tab the user already has open.


Each Add a step tile

The labels below are the ones on Build. Hidden/internal screens can appear in pickers because workflows can send people there on purpose.

Create record

Step: Create record (create_notion_record)

Adds a new page to a connected Notion database or app sheet. It does not edit the row that started the run.

Fields

  • Data source — the database that should receive the new row. Search by name. The builder saves sheet_id.

  • Field mappings — destination field plus Event data (payload.name) or Fixed value (Open).

Output later steps can read: context.<step_id>.record_id, context.<step_id>.sheet_id.

Example: Intake submitted → Create record on Tasks → Task title = payload.name, Status = Open.

What users see: Nothing from this step itself. They see a later Show message, email, or a new row after they refresh.

Do not use to change the triggering row. That is Update record. This step is background work: put Refresh / Show message before it if the open tab must update first.

Update record

Step: Update record (update_notion_record)

Changes fields on an existing row. The default record is payload.record_id (the form or button’s current record).

Fields

  • Data source — the database that owns the row.

  • Record reference path — usually payload.record_id, or context.<earlier_step>.record_id after Create record.

  • Field mappings — what to write.

Output: context.<step_id>.record_id, sheet_id, updated_count.

Example: Reviewer clicks Mark reviewed → Update record on Requests → Status = Reviewed, Reviewed by = payload.user_email.

What users see: The Notion row changes. The open tab updates only if Refresh data follows this step and Live screen updates is on (or this is still in the inline path).

Do not use without a record id. A completed run with updated_count: 0 wrote nothing. If a later step writes the same sheet, a Record updated trigger on that sheet can loop — add a Condition or use Status changed.

Step: Create related records (create_related_records)

Creates several child rows linked to the trigger record — photo checklist slots, punch-list items, one row per label.

Fields

  • Related data source — the child database (for example Photos).

  • Relation field (link to parent) — the relation/reference on the child that points at the parent.

  • Label / name field — text field that receives each label (Roof, Exterior).

  • Checklist labels — one related row per label. Enter to add; click a chip to remove; or paste many lines.

  • Optional parent data source — after children are created, write the parent (for example Status → Scheduled).

  • Optional notes field + Notes text — the same static text on every child.

Example: Assign Job form submitted → Create related records on Photos → relation = Job, labels = Roof / Exterior / Electrical.

What users see: New child rows after sync. Not a toast unless you add Show message.

Do not use if the child database has no relation to the parent. Do not also fire Record created on the child sheet for the same write unless you want a second workflow per slot.

Notify

Step: Notify (send_notification)

Sends email, an in-app Notification Center item, or a fire-and-forget webhook POST. It does not wait for Make to finish.

Fields

  • Channel — Email (default), In-app, or Webhook. SMS is stubbed and fails validation.

  • Preference category key — optional; honors App audience & notification prefs.

  • Bypass preference policy — only for critical/system mail.

  • Recipients or Endpoints — emails, or HTTPS URLs when Channel is Webhook.

  • Subject and Body — text or {{payload.fields.fieldId}} tokens. For webhook, body is a field inside the posted JSON.

Output: channel, recipient_count, sent, delivery_status (sent or simulated in local dev).

Example: Form submitted → Notify → Email reviewer@example.com → subject New intake received.

What users see: An email, or a Notification Center item. Webhook recipients see nothing in the app.

Do not use when Make must return JSON and Refresh the same tab. That is Call webhook and wait. Notify after Create/Wait is background-only.

Call webhook and wait

Step: Call webhook and wait (call_webhook_and_wait)

POSTs an HTTPS URL (usually Make), waits for a JSON Webhook response, then later steps (Refresh, Open screen, Show message) can run on the same open tab.

Fields

  • Webhook URL — Make (or other) hook URL. Make’s scenario must end with Webhook response JSON.

  • Sync timeout (ms) — default 8000, max 12000. After ~8s the portal can keep waiting in the background (~45s). A 4xx/5xx in the window errors and does not refresh.

  • Headers JSON — optional custom headers.

  • JSON body — defaults to the trigger payload. Response JSON is context.<step_id> for later Open screen.

Example: Approve button → Call webhook and wait → Make looks up pricing → Webhook response → Refresh data → Show message Updated.

What users see: Working… on the tab, then the refresh/message if Live screen updates is on and Make returned JSON. If Make never responds, they are asked to refresh.

Do not use for “just email someone.” Use Notify. Do not use Notify → Webhook for this job. Inbound webhooks Make fires at NotionApps still cannot refresh an already-open tab.

Details: Webhooks and Integrations, Workflow vs Messaging Webhooks.

Open screen

Step: Open screen (open_screen)

Navigates the current user to another app screen during the same interaction.

Fields

  • Screen — search by name. Saves screen_id. Hidden/internal screens can appear.

  • Presentation — Primary navigation (replaces the stack) or Modal (opens over the current screen).

  • Record reference path — optional. Default payload.record_id, or context.<create_step>.record_id.

Example: Submit for review → Open screen → Review detail, record = payload.record_id.

What users see: The destination screen, if this step runs in the inline path.

Do not use on a scheduled or inbound-webhook run with nobody looking. Put this before Create, Notify, or Wait, or after Call webhook and wait when Live screen updates is on.

Show or hide control

Step: Show or hide control (set_control_visibility)

Shows or hides one control for this session. It does not change the published screen definition.

Fields

  • Control — button, input, or display. Suggestions include type and screen.

  • Visibility — Show control or Hide control.

  • Screen — optional. Blank = current / last opened screen.

Example: Priority is High → Show or hide control → show Reviewer notes.

What users see: The control appears or disappears on the open screen (inline path only).

Do not use to permanently remove a field. Edit the screen. Put this before background steps.

Set control text

Step: Set control text (set_control_text)

Replaces the visible text of a label, heading, button, or display control for this session.

Fields

  • Control — the control whose text should change.

  • Text source — Event data (payload.*) or Fixed text.

  • Screen — optional. Blank = current screen.

Example: After submit → Set control text on a status heading → Submitted.

What users see: New copy on that control (inline path only).

Do not use to write a Notion property. That is Update record. This never persists after a full reload unless something else wrote the data.

Refresh data

Step: Refresh data (refresh_screen_data)

Tells the live app to reload rows it already knows how to show. It writes nothing.

Fields

  • Screen — optional. Blank = current screen.

  • Data source — optional. Use when only one sheet should reload.

Example: After Update record or Call webhook and wait → Refresh data → current details screen.

What users see: Updated values on the open tab when Live screen updates is on (or the step is still inline). When the switch is off, Refresh after Update cannot update the tab they already have open.

Do not use as the first step. Put the write (Update, Change status, Assign user, or Call webhook and wait) first. Create / Notify / Wait before Refresh makes Refresh background-only.

Show message

Step: Show message (show_message)

Shows a short toast on the current screen. Not email.

Fields

  • Message — keep it short.

  • Tone — Info, Success, Warning, or Error.

Example: After approve + Refresh → Show message Request approved. (Success).

What users see: The toast, if this step is still inline.

Do not use for email or Notification Center. That is Notify. Put this before Create / Notify / Wait, or after Call webhook and wait.

Stop action

Step: Stop action (block_interaction)

Stops the app’s default follow-through for this tap (button behavior, post-submit redirect/clear).

Fields

  • Message — optional explanation shown when the action stops.

Example: Priority missing → Condition fails path → Stop action → Choose a priority before sending.

What users see: The default navigation does not happen; they may see the message.

Do not use after a form save if you only wanted a toast — the row is already saved. Use Show message. For button clicks it prevents the normal button behavior. Inline path only.

Publish message

Step: Publish message (publish_message)

Publishes on Application Messaging. Can create a Decision or Work Queue card. This is not Notify email and not SMS.

Fields

  • Channel — app Messaging channel, or Custom….

  • Topic — presets such as client.review.requested (Decision) and client.intake.submitted (Work Queue).

  • Create inbox item — None, Task, or Decision.

  • Recipient routing — Decision audience/tenant, role pool, user field, or a specific email.

  • Title / Message / Summary — guided copy; {{payload.path}} tokens allowed. Payload JSON is advanced.

  • Preference category — same idea as Notify.

Requires Messaging entitlement.

Example: Status becomes With Client → Publish message → topic client.review.requested → Decision card for the client audience.

What users see: A Decision, Work Queue, Conversation, or Notification Center item when those screens exist — not an email unless you also Notify.

Do not use if you only need email. Use Notify. This is background work for the open tab.

Topic names: Topics that perform work.

Send request

Step: Send request (send_message_request)

Publishes a request on Messaging and parks the run until a reply or timeout.

Fields

  • Same channel / topic / inbox / routing / title fields as Publish message.

  • Timeout — default one hour (timeout_seconds).

  • Reply-to channel when you need a specific return path.

Example: Send to Needs Review → Send request → wait for client.decision.approve / changes_requested / reject.

What users see: The Decision / queue item. The run shows waiting on Activity until they reply.

Do not use for a one-way announce. That is Publish message. After resume, use Condition on context.__workflow_rehydration.payload.decision.

Send reply

Step: Send reply (send_message_reply)

Publishes a reply to the triggering message or configured correlation.

Fields

  • Topic — often a decision reply preset.

  • Reply status — for example completed.

  • Payload (guided or JSON).

Example: Reviewer approves on a Decision screen → Send reply → client.decision.approve so the waiting request run wakes.

What users see: The waiting request continues; the requester may see the next step (status change, email).

Do not use on a form-submit workflow that never received a message.

Wait for message

Step: Wait for message (wait_for_message)

Pauses the run until a matching channel/topic arrives (or timeout). Use after Publish message / Send request, or when another app will POST to messaging intake.

Fields

  • Channel

  • Primary topic

  • Wake on any of these topics — multi-select (topics[]). Decision presets: client.decision.approve, changes_requested, reject (and supervisor variants).

  • Correlation — default context.correlation_id.

  • Timeout seconds — default 3600.

Example: Publish client.review.requested → Wait for message on any client decision topic → Condition on the decision.

What users see: Nothing new until the reply. Activity shows waiting. Then Rehydrate or the inbound message continues the run.

Do not use for “wait two hours then email.” That is Wait. Topics must match the Decision reply topics exactly.

Condition

Step: Condition (condition)

Checks event data or earlier output. Multiple rules must all match. A linear Condition records context.<step_id>.matched and continues. Named yes/no paths need branch config (templates / API Branch).

Fields

  • Rules — each has a path (payload.priority, context.step_update.updated_count), operator, and optional value.

Operators: equals, not_equals, contains, is_empty, is_not_empty, greater_than, less_than, before, after.

Example: Only continue when payload.priority equals High.

What users see: Nothing. Later steps run or skip.

Do not use as a substitute for trigger Where it happens. Bind the trigger tightly first.

Wait

Step: Wait (wait)

Pauses on a clock, saves context (dehydrate), and continues later (rehydrate).

Fields

  • Delay seconds — relative. 3600 is one hour; 86400 is one day.

  • Wait until — optional absolute ISO time (2026-07-01T14:00:00.000Z).

  • Timer escalation step — optional step if the timer fires before someone clicks Rehydrate.

Example: After submit → Wait 86400 → Notify manager if still open.

What users see: Nothing during the wait. Activity shows waiting and Rehydrate. Builder Rehydrate resumes the normal next step (not escalation). Timer expiry uses the escalation step when set.

Do not use when the next step must happen in the same tap. Put Show message / Refresh before Wait.

End

Step: End (end)

Marks the path complete. No fields.

What users see: Nothing. The run completes.

Do not use as required. The last step with no next step also finishes. End is for a clear canvas and Activity timeline.


Recipe-only steps (not on Add a step)

Wizards such as Send to Needs Review insert these. The inspector still edits them like Update record.

Change status

Step: Change status (change_status)

Writes a status/select on the current record. Same Data source, Record reference path, and Field mappings as Update record — typically one status field.

When Live screen updates is on, Change status → Refresh → Show message updates the open details tab (Approve-style buttons).

Do not use if several unrelated fields must change in one step. Use Update record.

Assign user

Step: Assign user (assign_user)

Writes a person / assignee field. Same picker shape as Update record.

Do not use to send email. Pair with Notify if the assignee should be told.

Branch

Step: Branch (branch)

Multi-way split with labeled conditions. The Add a step grid exposes Condition; Branch appears on recipe/API routes. If no branch matches, the executor uses next_step_id.


What users see

Nothing from Create, Wait, Condition, End, or fire-and-forget Notify except the result (a new row, an email, a later toast).

They see Open screen, Show or hide, Set control text, Refresh, Show message, and Stop action only when those steps are still in the live path.

When Live screen updates is on: Update / Change status / Assign user / Call webhook and wait can Refresh / Open screen / Show message on the tab they already have open.

When it is off, Refresh after Update cannot update that tab. Create, Wait, and Notify stay background-only either way. Inbound webhooks into NotionApps cannot refresh an already-open tab.

Limits and plans

  • Messaging steps need Messaging entitlement.

  • Create related records needs a real relation property.

  • Call webhook and wait needs a reachable HTTPS URL and a JSON Webhook response.

  • Update / Change status on the same sheet as a Record updated trigger can loop — add a Condition.

  • Simulate / Test workflow still create runs unless the tool says otherwise.

Example

Inspection Assign Job form → form_submitted → Create related records (photo slots) → Notify inspector. Put Show message before Create if the submitter should see a toast in the same tap.

Approve on an open details tab (Live screen updates on): Change status → Refresh data → Show message.

Wait for Make, then refresh the same tab: Call webhook and wait → Refresh data → Show message.

Fix problems

Symptom
Likely cause
What to do

Submit works but no toast / no refresh

Show message or Refresh is after Create / Notify / Wait

Move live-screen steps above background steps, or use Call webhook and wait + Live screen updates

Refresh after Update does nothing on the open tab

Live screen updates is off

NotionApps turns the switch on from Workflow Operations → Controls

Call webhook and wait shows Working… then asks to refresh

Make did not return Webhook response JSON in time

End the scenario with Webhook response. Do not use Notify → Webhook

Create/Update validation error

Missing data source or field mappings

Choose the sheet and at least one mapping

Related records create nothing

Wrong relation field

Relation must point at the parent record

Double runs

Update wrote a sheet that also has Record updated

Remove one trigger or add a Condition

Messaging step does nothing

Entitlement or topic mismatch

Match Decision reply topics exactly. See Topics that perform work

Email simulated

Local development

Expected locally. Staging/prod actually send

Trigger catalog. Start From a Wizard. Workflow Foundation User Guide. Workflow How-To Guide. Webhooks and Integrations.