Workflow block library
Choose the steps that implement your customer journey: the trigger that starts it, the context it needs, the decision that changes its path and the action that follows. This reference describes what each workflow block accepts and what later steps can use from its result.
There are 21 step types in five categories. This page covers all of them: what each one is for, what you fill in, and what it leaves behind for later steps. It ends with the empty placeholder a step starts as before you give it a type.
How to read an entry
Every entry lists Inputs, the fields you fill in on the step's side bar, and Outputs, the values the step adds to the run.
Outputs are the part worth reading first. A step passes its result on under a name, and later steps use that name to reach it. You set it in the collapsed Rename what this step passes on section, which holds two fields on every step that produces something:
- Name: what later steps call this value. Each step suggests one, so you rarely touch it. The field's own note says it is "Only worth changing when two steps would pass on the same name."
- Note for maxinja: an optional line describing the value, so the assistant knows what it is looking at.
Two steps using the same name is not an error, and the editor does not stop you. The later step wins, and the earlier value is gone for everything downstream. The editor warns you and names the other step: "… already passes on 'x', and this step overwrites it for everything downstream. Give one of them a different name."
Every step's side bar opens the same way: the maxinja composer at the top (Build this step maxinja...), then the step's title and an optional Add description, then the step's own fields. There is no save button. Edits settle on their own, and none of them touch anything live until you publish.
Where a field asks you to describe logic in plain language, type @ to pick a value the run carries rather than typing its name from memory.
Triggers
A trigger decides what starts a run. Every workflow needs at least one, nothing can connect into a trigger, and each has a single Then output.
A trigger only listens while the workflow is published and not paused, and only the currently published version listens. See Manage workflows.
Scheduled Timestamp
Starts one run at a moment you choose.
Inputs
- Trigger time (optional): pick a date and a time. Leave it empty to fire immediately once the workflow is published. Times are in your local time zone.
Outputs
Nothing. A run from this trigger starts empty, so the steps after it have to fetch whatever they need with a Find Records or Query step.
Notes
Publishing is what arms it, and it fires exactly once. Republishing arms it again, so a workflow republished five times before the moment arrives has five runs waiting.
A time already in the past never fires. The step warns you rather than blocking the publish: "The timestamp is set in the past, so it will never fire."
Record Created or Updated
Starts a run when a contact or object record is created, or updated into the criteria you set.
Inputs
- Record type: the sentence reads When a … is created or updated. The picker lists your Contacts and Objects schemas.
- In segment (contact schemas only): a segment, or All <Schema> to write your own condition instead.
- Condition (only when no segment is selected): describe what narrows the trigger, for example "Only when the status becomes active". Leave it empty to start on every create and update.
- Name and Note for maxinja.
Outputs
The triggering record, under Name. It is a live reference, so later steps read it fresh rather than working from a copy taken at trigger time.
Notes
Records have to enter the criteria. On creation, the run starts if the record already matches. On an update, it starts only if the record did not match before and matches now. Saving a record that already matched changes nothing, so this trigger does not fire again and again for the same contact.
Picking a segment replaces the condition: membership becomes the condition, and the Condition field disappears.
With no segment and no condition, this fires on every create and every update of that record type, including bulk tag, subscription and topic changes and unsubscribes. One bulk import becomes one run per record, and every step of every run is billed. See Workflow billing.
The condition is compiled once when you write it, not on every run. See AI in workflows.
Record Deleted
Starts a run when a contact or object record is deleted.
Inputs
- Record type: the sentence reads When a … is deleted, over your Contacts and Objects schemas.
- In segment (contact schemas only): a segment, or All <Schema>.
- Condition (only when no segment is selected): checked against the record's last data before it was deleted, for example "Only when the status was active".
- Name and Note for maxinja.
Outputs
The deleted record's last data, under Name. Because the record is gone, this is a plain snapshot of values rather than a live record: nothing re-reads it, and it carries no computed fields.
Notes
Every matching deletion starts a run. There is no entering rule here, unlike Record Created or Updated.
Event Fired
Starts a run from the event you choose. The selector can bind a workspace event schema or a defined collected event. These have different configuration and publication behavior.
Inputs
- Event type: When a … fires selects a workspace event schema or a collected event.
- For a collected event, inspect its source, definition and revision before selecting it.
- Condition (workspace event schemas): optionally narrows the event data, for example "Only when the order total is over $100". A collected event uses its selected definition instead of this generated condition.
- Name and Note for maxinja. A new step already suggests
event.
Outputs
The fired event, under Name. A collected event carries the matched payload and its customer or object identity; the runtime keeps that subject attached to the run for later history decisions.
Notes
There is no segment option here. Segments filter contacts, and this trigger watches events.
Workspace events can arrive from the API, integrations or a Fire Event step. Collected events arrive through their configured source and definition.
A collected-event trigger requires an enabled definition and pins its revision at publication. Later definition edits do not silently rewrite that published trigger; review and republish to adopt the change. Publication establishes a source frontier, so this is not a backfill of historical matches. Currently a workflow can have up to 10 collected-event triggers and a workspace up to 50 active collected subscriptions.
The identified customer or object must still exist in the same workspace and schema when the collected event is delivered to the workflow. Durable delivery deduplicates the same receipt, while separate real events can still start separate runs.
Email Event
Starts a run on email activity anywhere in your space.
Inputs
- Email events: pick one or more of
email.scheduled,email.sent,email.delivered,email.delivery_delayed,email.opened,email.clicked,email.bounced,email.rejected,email.sending_failed,email.complained,email.unsubscribed. - Recipient contact schema (optional): the contact schema whose members you want to react to, or No contact matching.
- Condition (optional): narrows it further, reading the recipient contact, the sent email, and the event, for example "Only opens from a mobile device".
- Name and Note for maxinja, as a single base name.
Outputs
Three values built from one base name. With the base welcome they are welcome_contact, welcome_email and welcome_event. With no base set, they are contact, email and event. The contact value exists only when a Recipient contact schema is set.
Notes
The step's own note is the important part: "This trigger watches every email this workspace sends." Emails from any workflow, broadcast or one-off send are in scope. To react only to the mail one particular step sent, use that step's Email events branch instead. See Send Email.
Recipient contact schema filters, it does not just add context. Set it and the trigger fires only for emails whose recipient is a contact in that schema. Leave it empty and it fires only for emails sent to plain addresses with no contact behind them.
Select no event types and the workflow still publishes, with a warning, but the trigger can never fire. Pick at least one.
Trait Threshold
Starts when a stored computed attribute, also called a Trait, crosses a value you choose. Use it for a change such as a customer's purchase total entering a high-value band or an activity count falling below your engagement threshold.
Inputs
- Trait: an enabled computed attribute. Manage computed attributes opens the definitions.
- Comparison and Value: the threshold to test. Numeric traits support ordered comparisons; text and boolean values use equality or inequality. Timestamp thresholds need an explicit time zone.
- Direction: entering the matching state, leaving it, or both.
Outputs
A fixed threshold value carries the subject, trait identity and generation, dimension, previous value, current value, direction and crossing time. This is the evidence for the transition; later steps should not replace it with today's trait value when explaining why the run began.
Notes
Publication pins the trait revision and establishes a baseline. Customers who already match do not all trigger immediately. Later completed calculations produce crossings, including a change caused by a rolling window expiring. This is different from a record-update trigger: a trait can change when time passes without anyone editing the contact.
To message the people who already match at launch, make that a separate, deliberately reviewed audience. Do not expect the threshold trigger to backfill them.
Incoming Webhook
Starts a run when an outside system posts JSON to this step's own URL.
Inputs
- Trigger URL: read-only. Use the generate button to create one, then copy it. The button becomes Renew URL once a URL exists.
- Expected payload: the shape of the JSON body you will send. Paste a sample request and let maxclicks work the shape out, build it by hand, or generate it with AI.
- Name and Note for maxinja.
Outputs
The parsed request body, under Name, after it has been checked against Expected payload.
Notes
The sending system posts an HTTP POST with a JSON body and an Authorization: Bearer header carrying an API key for this space. The URL only goes live once you publish.
Requests whose body does not match Expected payload are rejected, so later steps can rely on the shape. The workflow will not publish without a payload shape set.
Treat the trigger URL like a secret. Renewing it generates a new one that only takes effect at the next publish: until then, outside services keep firing the currently published URL and the new one is not live yet.
Duplicating a workflow gives the copy its own URL, so a copy never steals the original's traffic.
Enhancers
An enhancer adds a value to the run and hands it on. Each has a single Then output.
Find Records
Looks contact or object records up mid-run and puts them on the run.
Inputs
- Record type: the sentence reads Find … records, over your Contacts and Objects schemas.
- In segment (contact schemas only): a segment, or All <Schema>. A live count of matching records appears underneath.
- Filter (only when no segment is selected): describe which records to bring in, for example "Customers who purchased in the last 30 days".
- Process each record separately: see the note below.
- Name and Note for maxinja.
Outputs
The matching records, under Name. Checked, later steps see one record at a time. Unchecked, they see the whole list as a single value.
Notes
Picking a segment replaces the filter and keeps following that segment's own definition, so the Filter field disappears.
With neither a segment nor a filter, this brings in every record of the type, every run. The editor warns rather than blocking: "Filter is not specified, which may impact performance." Scope it deliberately, especially with Process each record separately checked, where a schema of 50,000 contacts becomes 50,000 runs.
Checked and nothing matched, the run ends at this step. Unchecked and nothing matched, the run carries on with an empty list. Leave it unchecked when a later step has to act on all the records at once.
Set Value
Works a value out from what the run already carries.
Inputs
- Value: describe what to compute, for example "The number of days since the contact signed up". Reshaping, math, text building, date arithmetic, assembling lists.
- Value shape: the JSON shape of the result, so later steps know what they are getting.
- Name and Note for maxinja.
Outputs
The computed value, under Name, in the shape you declared.
Notes
This step never touches the database. It only works with values the run already has. To fetch something the run does not carry yet, use Query or Find Records.
Name is required here: the workflow will not publish without it.
Query
Runs a database lookup and adds its answer to the run.
Inputs
- Query: describe the answer you want, for example "The count of contacts created this month". A lookup, a count, an aggregate, or a deep read across records, events and email activity, optionally parameterized by what the run carries.
- Result shape: the JSON shape of the answer.
- Name and Note for maxinja.
Outputs
The answer, under Name, in the shape you declared. It is checked against that shape when the step runs.
Notes
This is the way to bring in data the run does not have yet. Where Set Value reshapes what is already there, Query goes and reads.
AI
Runs your own prompt against a model you pick, with the slice of run data you choose as context.
Inputs
- Expected data: which of the incoming values this step reads, and which related records to load with them.
- Prompt: the step's standing instruction, for example "Research the company's latest funding round and report the amount, lead investor, and date." This is not a message to maxinja: it runs on every run.
- Result shape: the JSON shape the answer must follow.
- AI settings:
- Model: required. Pick the model that runs this step.
- Thinking: required. Off, Low, Medium or High. Deeper thinking gives better judgment and makes every run slower and pricier.
- Web search: lets the model search the web, billed per search.
- Workspace data access: read-only query tools over your workspace's data.
- MCP servers: appears when your space has servers registered.
- Name and Note for maxinja.
Outputs
The model's answer, under Name, in the shape you declared. It is checked against that shape before the run continues.
Notes
This is the only enhancer that calls a model on every single run, on top of the per-step fee. Web search adds a charge per search, and Workspace data access makes every run markedly slower and pricier, because the model reads a description of your workspace before it can even phrase a query. All of it lands on every run, not just the first. See AI in workflows and Workflow billing.
Model and Thinking are pinned on the step itself, with no workspace default behind them. Editing your workspace's AI settings can never change what a published workflow does.
For Each
Splits the run over a list the run already carries.
Inputs
- Data path: the list to loop over.
- Item type or Item shape: read-only, worked out from the path.
- Expected data: read-only, the one value the path enters.
- Name and Note for maxinja.
Outputs
One item, under Name, per run. Reusing the list's own name is fine: the item takes the list's place for everything downstream.
Notes
This step always fans out, with no checkbox to turn it off. Each item continues the workflow as its own independent run, and an empty list ends the run right here.
Point Data path at something that is not a list and the editor only warns you, but the run fails when it gets there. Check the Item type or Item shape line: if neither appears, the path is not a list.
Controllers
A controller routes or paces the run. None of them add anything to the run: whatever came in carries straight on.
If/Else
Sends the run down one of two paths.
Inputs
- Condition: describe when a run should take the If true branch, for example "The order total is over $50", or a judgment call like "this product fits the customer's interests". Anything else takes If false.
- AI on every run (Model, Thinking): appears only while the condition is uncompiled.
Outputs
Nothing. The run carries its values on unchanged, down whichever branch it took.
Notes
The two outputs are If true and If false. Leaving one unconnected is legitimate: a run that takes it ends there.
Ordinary condition logic evaluates the configured incoming data. Put a Query or Find Records step before it when more record context is needed. The dedicated Customer event history mode below reads a bounded event collection for the run's subject.
Leave the condition uncompiled and an AI model weighs it on every single run, which adds latency and cost to each one. maxclicks warns you at publish and makes you pin a Model and Thinking depth on the step first. Compiling avoids a model interpretation on each run. The step fee remains, and the result still depends on the supplied data. See AI in workflows.
Customer event history
If the branch is about a customer's recorded activity, choose Customer event history in If/Else instead of describing a general condition. Select the event collection and configure:
| Control | Decision it defines |
|---|---|
| Condition | Has occurred or Has not occurred. |
| Look back (days) | The 1–30-day interval ending when this step captures its decision window. |
| Allow late events (seconds) | How long to allow events to arrive after that window closes; absence requires at least 60 seconds, with up to one day supported. |
| Match | This customer/object, or the same correlation ID as the triggering event. |
The run must identify exactly one customer or object. Correlation matching also needs a correlation ID on its trigger. The runtime saves the time window and identity evidence, waits if event storage is behind, and reuses the accepted result during recovery. It does not turn an unavailable history service into a false "has not occurred" decision.
For example, after a signup wait, check whether this customer created a project in the relevant window. For an order-specific follow-up, correlation matching can distinguish this purchase from another purchase by the same customer. See event-history decisions.
Switch
Sends the run down exactly one of any number of named paths.
Inputs
- Case selection: describe how to pick one of the cases below, for example "Route by the order's plan tier: free, pro, or enterprise".
- Cases: one row per outgoing path. Add case appends one, and each row has Move up, Move down and Remove case. Every case needs a value, and no two can be the same.
- AI on every run (Model, Thinking): appears only while the selection is uncompiled.
Outputs
Nothing. The run carries its values on unchanged, down the case that was picked.
Notes
There is no built-in otherwise path. The selection always lands on a case by its value, and a selection that matches none of your cases fails the run. Make the cases exhaustive, or add an explicit catch-all case (something like other) and mention it in Case selection.
Removing a case removes its path and the link going out of it. Reordering cases is cosmetic, but adding, removing or renaming one changes what the selection has to land on, so you will need to compile the selection again.
At least two cases are needed to publish.
Delay
Holds the run and releases it later.
Inputs
- Delay: describe the wait, for example "Wait 3 days" or "until 9am in the contact's local time zone". A fixed duration, a target moment, or one worked out per run from what the run carries.
- AI on every run (Model, Thinking): appears only while the delay is uncompiled.
Outputs
Nothing. The run carries its values on unchanged.
Notes
There is no duration box and no date picker. You describe the wait, and whether it comes out as a duration or as a moment follows from your description.
Delays are durable. The duration or target time is evaluated once and the deadline is saved, so a restart does not repeat the interpretation or reset the wait. Pausing the workflow parks the run but keeps that deadline; if it passes during the pause, the run can continue after resume.
A wait that depends on data the run does not carry needs a Query or Find Records step before it.
Actions
An action changes something, inside maxclicks or outside it. Each has a single Then output, except Send Email, which has two.
Create or Update Records
Builds contact or object records out of the run and writes them.
Inputs
- Record type: the sentence reads Create or update … records, over your Contacts and Objects schemas.
- Records: describe the records to write, for example "a contact from the event payload email and name". One record or a whole list.
- Records shape: read-only, telling you whether your description produces one record or a batch.
- Process each record separately: appears when the description produces a list.
- Name and Note for maxinja.
Outputs
The records as they now stand after the write, under Name. These are the stored records, so they carry everything, not just the fields you set.
Notes
Existing records are matched and updated rather than duplicated. Contacts are matched by their id, then userId, then email, then phone. Objects are matched by their id, then external id. Anything matching nothing is created fresh.
An update merges. Fields your description does not mention keep the values they already had.
Contacts this step creates are subscribed to every communication channel unless your description sets their subscriptions explicitly. If these contacts did not knowingly opt in, say so in the Records description, or you are adding people to your marketing audience without their consent. See Contacts.
Writing records sets off Record Created or Updated triggers and webhooks elsewhere in your space, including in this same workflow. Creating contacts also changes the contact count your plan is billed on.
With Process each record separately checked and nothing written, the run ends at this step.
Delete Records
Permanently deletes contact or object records.
Inputs
- Record type: the sentence reads Delete … records, over your Contacts and Objects schemas.
- In segment (contact schemas only): a segment, or All <Schema>.
- Filter (only when no segment is selected): describe which records to delete, for example "contacts unsubscribed over a year ago".
- Process each record separately: required here, so set it deliberately.
- Name and Note for maxinja.
Outputs
The deleted records' last data, under Name, as a plain snapshot rather than live records.
Notes
Deletion is permanent. The step's own banner says it: "Deletion is permanent. The selected records are removed when this step runs, only their last data flows on to later steps." There is no undo and no recycle bin.
With neither a segment nor a filter, this deletes every record of the chosen type, on every run that reaches it. maxclicks only warns you at publish: "No filter is specified, so this step permanently deletes every record of the selected type." It does not stop you.
A segment ignores what the run carries. It deletes every contact in that segment each time any run reaches the step, not just the contact this run is about.
Deleting sets off Record Deleted triggers and webhooks, and deleting contacts changes the contact count your plan is billed on.
Fire Event
Fires one event out of the run.
Inputs
- Event type: the sentence reads Fire a … event, over your event schemas.
- Event data: describe what the event carries, for example "a purchase-completed event with the order total", including who or what it belongs to.
- Name and Note for maxinja.
Outputs
The fired event, under Name, as it was stored.
Notes
One event per run, never a batch. Events are immutable: nothing this step does ever updates an existing one.
The event lands on its record's timeline, and it sets off Event Fired triggers and webhooks. This is the supported way to chain one workflow into another, since a workflow cannot loop back on itself.
Any attribute with auto-fill turned on is filled in for fields your description leaves out, and an AI-backed auto-fill spends credits each time.
HTTP Request
Calls an outside endpoint and adds the response to the run.
Inputs
- Expected data: which of the incoming values this request reads, and which related records to load with them. The templates below interpolate it.
- Method: GET, POST, PUT or DELETE. GET hides the body, content type and signature fields.
- URL: required. Supports Liquid, so
{{ }}drops in values from earlier steps. Use the{ }button rather than typing a reference from memory. - Headers (optional): edit as rows, or switch to JSON for headers that only exist under a condition.
- Content type (optional): sent as the
Content-Typeheader. - Body (optional, not on GET): what the request sends, with Liquid in its text values.
- Body signature (optional, not on GET): generate a secret and maxclicks signs each request so your endpoint can verify it really came from you.
- Response shape (optional): the JSON shape the response must follow.
- Name and Note for maxinja.
Outputs
Under Name: the parsed response body under data, alongside status, statusText and headers.
Notes
Only a 2xx response counts as success. The response must be JSON and, when supplied, match Response shape. The request has a 30-second timeout, a 1 MiB request and response limit, and does not follow redirects.
The request includes an Idempotency-Key for this run and step occurrence; the receiving service should honor it. A recorded successful response can be reused during safe infrastructure recovery. A timeout or uncertain result is not blindly replayed. Check the receiving system before re-triggering: it may have completed the action even though maxclicks could not validate the response.
Rotating the signing secret only takes effect at the next publish. Until then this step keeps signing with the currently published one, so teach the receiving endpoint the new secret before you publish, not after.
To verify a signature, hash the raw body exactly as it arrived. Parsing the JSON and re-serializing it changes the bytes and breaks the check.
Requests can only reach public addresses on the internet, so this step cannot be pointed at a private or internal network.
Send Email
Sends this step's own email to one recipient.
Inputs
- Sends to: required. One contact, or one email address, taken from what the run carries. A list of contacts cannot be one recipient: put a For Each step in front, or set Find Records to process each record separately, so each one gets its own email.
- Email template: Edit opens the full email editor. Before a template exists you pick a starting point (Dynamic Composer, Plain Composer, MJML Code or HTML Code) and a Sending mode:
- Marketing: "Only subscribed contacts; manages the unsubscribe link."
- Transactional: "All contacts regardless of subscription; no unsubscribe link."
- Email event branch (optional): the email event types the second output should fire on.
- Name and Note for maxinja: appears once you select event types.
Outputs
- Then adds nothing. The run carries on when the email provider accepts the send. Delivery, opens and clicks happen later.
- Email events adds three values from one base name:
<name>_contact,<name>_emailand<name>_event. With no base set they arecontact,emailandevent. The contact value exists only when the email went to a contact rather than a bare address.
Notes
Consent is enforced, and a send that breaks it fails the run rather than going out anyway. Marketing content can never go to a bare email address, because there is no contact behind it, no consent, and no working unsubscribe link. Marketing content to a contact requires that contact to be subscribed to email, and to hold the communication topic when the template names one. Any suppression (unsubscribed, bounced, complained, manual or legal) blocks marketing content, and a bounce suppression blocks transactional content too.
Sending mode cannot be changed after the template is created, so pick it deliberately.
Personalization costs money per recipient. If the template carries Adaptyle instructions, each send makes its own AI call. Every send also draws on your plan's email allowance. See Workflow billing.
The Email events branch does not resume this run. Each event starts a brand new run entering this step on that second output, so it shows up separately on the Runs tab and is billed as its own run. Leave the event types empty and the branch never fires at all, and connecting it while empty blocks the publish.
The template belongs to this step alone, so editing it changes nothing else in your workspace. Publishing takes a copy of it, which is why runs already in flight on an older version keep sending the older email.
Decorators
Note
Documents the canvas for whoever opens this workflow next.
Inputs
- Content: the note itself, in Markdown.
- Description: an optional line describing what the note is about, which teammates and AI agents read first.
- Color: one of the badge colors. New notes start yellow.
- Last edited by: read-only, showing who saved it last.
Drag the bottom-right corner on the canvas to resize it.
Outputs
Nothing. A note has no connection points at all, so nothing can link into or out of it, and it takes no part in any run.
Notes
Editing a note still marks the workflow as having unpublished changes, even though nothing about a run has changed.
Placeholders
None
An empty step holding its place on the canvas until you give it a type.
Inputs and outputs
None.
Notes
You never pick this from the step type list. maxclicks creates one when you add a step from an outgoing connection point, and you then choose what it should be in the side bar.
A workflow still holding one will not publish: "This is a temporary step type, resolve it before publishing the workflow."
Processing many records
Three steps carry a Process each record separately checkbox: Find Records, Create or Update Records and Delete Records.
| Setting | Behavior |
|---|---|
| Checked | The run fans out: one independent run per record, and later steps see a single record at a time. If the step produced no records, the run ends there. |
| Unchecked | The run stays one run, carrying every record together as one list, and it continues even when the step produced nothing. |
For Each always fans out, one run per item, with no checkbox.
Fan-out is the biggest multiplier on what a workflow costs. Every run charges for every step it executes, so a step that fans out over 5,000 records turns one run into 5,000. Checking or unchecking the box also changes the shape of what later steps receive (one record, or a list), so steps after it may need reconfiguring.
Related
- Workflows: the canvas, runs, versioning, and the trigger API.
- Create a workflow: build the graph step by step.
- Manage workflows: publishing, pausing, and versions.
- Workflow runs: see what a step did, and why a run failed.
- AI in workflows: compiled logic, logic worked out per run, and the AI step.
- Workflow billing: what each step costs, and the concurrent run cap.
- Segments: reusable contact filters for triggers, Find Records and Delete Records.
- Evaluated attributes: computed fields your step logic can read.
- Templates: email content for Send Email.