👣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:
Step — the kind of work (create a row, send email, wait, open a screen).
Config — the fields for that type (data source, recipients, screen, topic, delay).
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
Open Automation → Workflows and select the workflow (or start from a wizard).
On Build, click an Add a step tile.
Name the step in plain language (
Create follow-up task, notStep 2).Fill the inspector fields below. Use Event data for
payload.*/context.*and Fixed value for literals.Put live-screen steps (Refresh, Show message, Open screen) before Create / Notify / Wait when the open tab must update.
Save, publish the workflow, and publish the app. Confirm the run on Activity.
Every control
These are the Add a step tiles on Build:
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):
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, orcontext.<earlier_step>.record_idafter 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.
Create related records
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, max12000. 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, orcontext.<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) andclient.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.
3600is one hour;86400is 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
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
Related
Trigger catalog. Start From a Wizard. Workflow Foundation User Guide. Workflow How-To Guide. Webhooks and Integrations.