# Glow Documentation — full text > Every published Glow documentation page, concatenated. Generated at build time from the same sources as llms.txt. > Pages: 128. Index with per-page URLs: https://docs.getglow.ai/llms.txt --- Source: https://docs.getglow.ai/build/action-steps/change-fields # Change Fields > Rename, add and drop fields on a record so the next step gets exactly the shape it expects. **Change fields** reshapes a record on its way between two steps. Rename a field the receiving system calls something else, add one it needs, drop the ones it should never see. > Dock: Data · Change · Takes: a record · Returns: the changed record It is the step that saves you from writing code purely to move data from one shape into another. > **Transforming single fields inline?** If you only need to clean, uppercase, format, or extract a specific field value before passing it to a downstream step, use [Data Transformation](/build/core-concepts/data-transformation) (e.g. `{{ 3.company | trim | upper }}` or `{{ 3.users | pluck:email }}`). Use the **Change fields** canvas step when reshaping entire records (renaming, adding, and dropping multiple keys simultaneously). ## Three passes, in a fixed order The step runs three passes, always in this order. | Pass | When it runs | What it does | | ----------------- | ------------ | ----------------------------------------------------- | | **Rename fields** | First | Each row gives a field's current name and its new one | | **Set fields** | Second | Each row sets a field to a value | | **Remove fields** | Last | Each row names a field to leave out of the result | Because renaming happens first, **a Set row can write to a name that only exists because a Rename above created it**. And because removal happens last, a field you set can still be removed in the same step. That suits a value you need briefly and do not want to pass on. ## Setting it up ### Add the step Open **Tools** in the dock and choose **Change fields** under Data. ### Point it at a record **Start from** takes the record you want to change, normally a reference to an earlier step such as `{{ 3 }}`. ### Rename what needs renaming Under **Rename fields**, add a row per field: its current name, then the name it should have. **Add Row** for each one. ### Set what needs setting Under **Set fields**, add a row per field with its value. The value can be a reference, so `{{ 1.email }}` or `{{ $today }}` both work. ### Remove what should not travel on Under **Remove fields**, name any field to leave out: usually an internal identifier, or a large blob you do not want carried into the next step. ## What it passes on The changed record itself, with your renames, sets and removes applied. Fields sit at the top level, so a downstream step reads them directly: ``` {{ 4.firstName }} {{ 4.greeting }} ``` Everything you did not mention travels through unchanged, keeping its type. ## Examples to copy ### Match a CRM's field names An enrichment API returns `company_name` and `employee_count`, but your CRM expects `Company` and `Headcount`. **Start from:** `{{ 3.ret }}`. Two **Rename** rows: `company_name` → `Company`, `employee_count` → `Headcount`. Nothing else needed. ### Stamp a record before storing it **Set fields:** `processedAt` = `{{ $now }}`, `source` = `Website form`, `workflowUrl` = `{{ $workflow_url }}`. The last one gives whoever finds the record later a link straight back to the workflow that wrote it. ### Cut a record down before sending it on An app returns fifty fields and the next system wants six. Use **Remove fields** for the big ones: the raw HTML of an email, an attached file, an internal id nobody downstream reads. A smaller record moves between steps faster and stays under the size a run may carry. ### Rename, then build on the new name **Rename:** `first_name` → `firstName`. **Set:** `greeting` = `Hi {{ 3.first_name }}`. The rename gives downstream steps the tidy `firstName`; the greeting is built from the source step's value. A `{{ }}` reference always reads an earlier step's stored output: a step cannot reference its own result while it is still being made. ## What's Next? - 👉 **[Workflow Data & Mapping →](/build/core-concepts/workflow-data)**: understand how values move from one Step to the next. - **[Parse JSON](/build/action-steps/parse-json)**: turn a JSON string into a record you can reshape. - **[AI Data Transform](/build/ai-features/ai-transform)**: reshape unstructured content when there are no fixed fields to select. --- Source: https://docs.getglow.ai/build/action-steps/code-execution # Code editor > Run Python inside a workflow: how to read earlier steps, which libraries are available, what the step passes on, and the limits it runs under. The Code editor step runs Python inside your workflow and passes on whatever your code prints. Use it for the calculations, reshaping and business rules that the visual steps do not cover. > Dock: Data · Dev tools · Returns: whatever your code prints **Look for it as "Code editor" in the dock.** That is the label it carries there, under **Tools → Data → Dev tools**. **Keyboard shortcut:** `t+c` > The step runs **Python 3.13**, with the standard library plus numpy and pandas > available. The Language field reflects that and is fixed. ## Setting it up ### Add the step Press `t+c`, or open **Tools** in the dock and select **Code editor** under **Dev tools**. ### Write your Python Reference earlier steps with `{{ }}` placeholders, on their own and without quotes. The section below covers the syntax. ### Print what the next step needs The step passes on what you print, not what you return. ### Run the workflow to see the result Open the step's **Executions** tab to read the output. ## Reading data from earlier steps Reference an earlier step by its number, the same as in any other field, and **write the reference on its own and unquoted**: ```python filename="main.py" customer_name = {{ 2.customer_name }} order_total = {{ 2.total }} products = {{ 3.$full_result }} ``` Each reference arrives as **a real Python value**, not as text that gets pasted into your program. A JSON object becomes a `dict`, a list becomes a `list`, and `true` / `false` / `null` become `True` / `False` / `None`. Nothing needs quoting or escaping, and a customer called `O'Brien` cannot break your code. `{{ 3.$full_result }}` gives you a whole step's output as one value, which is the usual way to get a list of records to loop over. ### "Reference is inside a longer piece of text" If the step fails with that message, this is the cause and the fix. A reference has to be a value on its own. Written inside a longer piece of text it has no correct meaning, so the step refuses to run rather than guessing: ```python filename="Rejected" print("Hello {{ 2.name }}") ``` Assign it first and build the text afterwards: ```python filename="Works" name = {{ 2.name }} print(f"Hello {name}") ``` The same applies inside an f-string or any other quoted run, and to every kind of reference, `{{ $var.KEY }}` and `{{ $secret.KEY }}` included. Assign first, then use the variable. Full placeholder syntax, including `{{ $now }}` and `{{ $var.KEY }}`, is documented in [Variable Reference Syntax](/reference/variable-syntax). ## Available libraries The step runs **Python 3.13** with the full standard library, plus two data libraries. Import them as normal: there is nothing to install and nothing to declare. ### The two data libraries | Library | Version | For | | ---------- | ------- | ----------------------------------------- | | **numpy** | 2.5.1 | Numeric arrays and mathematics | | **pandas** | 2.3.3 | Tables, grouping, joins and summarisation | Both versions are pinned rather than tracking the latest release, so the same program keeps producing the same result. ### The standard library, by what you came to do Everything in Python 3.13's standard library imports, which is far more than most workflows need. These are the modules worth knowing about: | You want to | Import | | ------------------------------------- | --------------------------------------------------------- | | Read or write JSON | `json` | | Do date and time arithmetic | `datetime` — and `zoneinfo` for time zones | | Match or replace text patterns | `re` | | Read or write CSV | `csv` (with `io.StringIO` for text you already have) | | Hash or sign something | `hashlib`, `hmac` | | Encode for an API | `base64`, `urllib.parse` for query strings and escaping | | Count, group or deduplicate | `collections` — `Counter`, `defaultdict` | | Averages, medians, standard deviation | `statistics` | | Money and exact decimals | `decimal` — avoids the rounding errors of ordinary floats | | Generate an ID | `uuid`, `secrets` | | Combine or chunk lists | `itertools`, `functools` | | Compress or unpack an archive | `gzip`, `zipfile`, `tarfile` | ```python filename="main.py" import json import re from datetime import datetime, timedelta from zoneinfo import ZoneInfo from collections import Counter import pandas as pd ``` ### Finding what you need Check the table above first: most of what people install a package for is already in the standard library. `datetime`, `re`, `csv`, `hashlib` and `json` between them cover the large majority of workflow code. If it is a package for calling a service (`requests` being the usual one), that work belongs in a step rather than in code; see [What to use instead](#what-to-use-instead) below. For anything genuinely missing, ask support. The library set is deliberately small so that runs stay reproducible, and additions are considered on request. ## What it passes on **Print what you want to pass on.** The step captures what your code writes to output. A bare `return` produces nothing downstream. ```python filename="main.py" import json print(json.dumps({ "final_total": final_total, "discount_applied": discount, })) ``` What you printed sits under `result.executionOutput`: ``` {{ 4.result.executionOutput }} ``` > **Note the `result.` in the middle.** This step nests its output one level > deeper than most, so `{{ 4.executionOutput }}` finds nothing. Insert the > reference from the data icon rather than typing it and you get the right > path. > **`executionOutput` is text, not an object.** You cannot reach into it with > `{{ 4.result.executionOutput.final_total }}`, because there is nothing to > walk into. To use individual fields downstream, print JSON as above and put a > [Parse JSON](/build/action-steps/parse-json) step after this one. ### Print JSON, not Python objects `print(my_dict)` produces Python's own formatting: single quotes, `True`, `None`. A Parse JSON step cannot read that. Use `json.dumps()` so the output is valid JSON. Class instances, lambdas and circular references cannot be serialised at all, so convert them to plain values first. ## What to use instead Your code runs on its own: everything it needs arrives through references, and everything it produces leaves through what it prints. Reaching out to the world is the job of the steps around it, not of the code. That keeps credentials out of a code box and every request visible in the run history. A few things people reach for out of habit therefore do not apply here, and each has a step that does the job better: | Habit | What happens | Use instead | | --------------------------------------- | ----------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `import requests` to call an API | The package is not available, and the network is unreachable regardless | An [HTTP Request](/build/action-steps/http-request) step before this one to fetch, and one after it to send. It handles authentication, retries and errors for you. | | Sending mail with `smtplib` | No connection | A Gmail, Outlook or SendGrid step from the dock's **Apps** group. | | Connecting to a database from code | No connection | The database step for that service, which keeps the credentials in your workspace. | | `open("out.csv", "w")` beside your code | `Read-only file system` | Write to `/tmp` for scratch space during the run, then print what matters. To keep a file, pass the contents to a Drive, S3 or email step. | | Reading a file an earlier run wrote | `FileNotFoundError` | Every run starts clean. Carry values between runs through the steps themselves, or store them in an app. | | `input()` to ask for a value | `EOFError` — nobody is at a keyboard | Reference the value with `{{ 2.field }}`, or collect it with a [Human Review](/build/action-steps/user-approval) step. | **This is the design, not a gap to work around.** A step that fetched its own data would hide that request from the run history, retry nothing when the API is down, and put an API key in a code box. Keeping the reaching-out in dedicated steps is what makes a failed run readable at three in the morning. ## Limits | Limit | Value | | -------------- | ------------------ | | Run time | 150 seconds | | Output kept | 100,000 characters | | Memory | 1.5 GB | | Network access | None | **Run time.** A program that passes 150 seconds is stopped and the step fails. Filter large lists before expensive work, and check that loops terminate. **Output.** Past 100,000 characters the result is stored truncated and marked with how many characters were dropped. A step printing a large dataset therefore passes on something that is no longer valid JSON. Print only what the next step needs. For a genuinely large result, print a summary and send the full data onward with a following [HTTP Request](/build/action-steps/http-request) or an app step such as Google Sheets. **Memory.** Beyond 1.5 GB the program stops with a `MemoryError`. Process records in batches rather than loading everything at once. **Network.** Your code cannot open connections, so `urllib`, `socket` and anything built on them will not reach a server. Fetch the data with an [HTTP Request](/build/action-steps/http-request) step before this one and read its result with `{{ N.$full_result }}`; to send something onward, print it and follow this step with an HTTP Request. ## When a run fails An uncaught exception fails the step and the branch stops. The error message carries what your program printed before it failed, including the traceback, so the step's **Executions** tab tells you which line went wrong. Guard optional data rather than assuming it is there — the chained `.get()` on the highlighted line is what stops a missing field failing the step: ```python filename="main.py" {2} payload = {{ 2.$full_result }} value = payload.get("response", {}).get("data", {}).get("value", "default") ``` Because a failed run stops the branch, there is no result carrying a flag to test: by the time a later step can read the output at all, the code ran. So the check worth adding on a critical path is not "did it succeed?" but "is what it printed the shape I expected?". Where the outcome drives money, deletion or anything else irreversible, follow the step with a [Condition](/build/action-steps/conditions) that asserts the shape and give the rest an error path. ## Examples to copy ### Work out a total ```python filename="main.py" import json order_total = {{ 2.total }} discount = order_total * 0.1 if order_total > 100 else 0 final_total = order_total - discount print(json.dumps({ "final_total": round(final_total, 2), "discount_applied": round(discount, 2), })) ``` ### Score and rank a list A workflow fetches product data and needs weighted scores before writing to a spreadsheet. ```python filename="main.py" import json products = {{ 3.$full_result }} scored = [] for product in products: score = (product["rating"] * 0.6) + (product["reviews"] * 0.004) scored.append({ "name": product["name"], "score": round(score, 2), "tier": "premium" if score > 4.0 else "standard", }) scored.sort(key=lambda p: p["score"], reverse=True) print(json.dumps({"ranked_products": scored})) ``` ### Group and summarise with pandas ```python filename="main.py" import json import pandas as pd orders = pd.DataFrame({{ 3.$full_result }}) by_region = ( orders.groupby("region")["amount"] .agg(["sum", "count"]) .round(2) .reset_index() ) print(json.dumps(by_region.to_dict(orient="records"))) ``` Follow any of these with a **Parse JSON** step reading `{{ 4.result.executionOutput }}`, and the result becomes addressable field by field. ## What's Next? - 👉 **[Parse JSON →](/build/action-steps/parse-json)**: turn printed JSON into fields that later Steps can select. - **[Helper Functions](/build/action-steps/helper-functions)**: use a ready-made conversion instead of maintaining code. - **[AI Data Transform](/build/ai-features/ai-transform)**: reshape unstructured content without code. --- Source: https://docs.getglow.ai/build/action-steps/combine # Combine > Bring two lists together: one after the other, or matched on a field they share. The **Combine** step takes two lists and returns one. It either places the second list after the first, or matches records from each on a field they have in common. > Dock: Data · Lists · Takes: two lists · Returns: one combined list Reach for it when the data you need is split across two places: contacts in one system and their orders in another, incidents from a ticketing tool and the service levels that apply to them. ```mermaid flowchart LR A[Get Customers] --> C[Combine] B[Get Orders] --> C C --> D[Send Summary] ``` ## Setting it up ### Connect two branches to it Combine takes exactly two incoming connections, each from a different step, and each supplies one of the two lists. It always waits for both branches before it runs; the **AND** chip on its input shows this. If you add a third connection, or a second one from the same step, the canvas declines it and says why. See [Merging Parallel Branches](/build/core-concepts/steps-and-the-canvas#merging-parallel-branches). ### Choose how to combine **Append lists** places the second list's items after the first list's. **Match fields** pairs records that share a value. ### Pick the two lists **First list** and **Second list** each read from one of the connected branches. The branch you pick for the first list is no longer offered for the second, so each list comes from its own step. You can also paste a JSON list into either field. ### Name the field to match on, if you are matching **Field in first list** and **Field in second list** name the field in each. They do not have to be spelled the same: `customer.id` on one side can meet `account.externalId` on the other. ## Deciding what happens to records with no partner **Keep unmatched records** is the setting that decides the shape of the answer, and it is worth understanding once rather than guessing. Two lists sharing a field `id`: ``` First list [{"id": 1, "name": "Lovelace"}, {"id": 2, "name": "Hopper"}, {"id": 3, "name": "Turing"}] Second list [{"id": 1, "total": 100}, {"id": 2, "total": 250}, {"id": 9, "total": 7}] ``` Records 1 and 2 exist in both. Record 3 is only in the first list, record 9 only in the second. | Setting | What comes back | | -------------------- | ------------------------------------------------------------------------------- | | **Matches only** | The two matched records, merged. Nothing else. | | **From both lists** | The two matches, then `{"id": 3, "name": "Turing"}` and `{"id": 9, "total": 7}` | | **From first list** | The two matches, then `{"id": 3, "name": "Turing"}` | | **From second list** | The two matches, then `{"id": 9, "total": 7}` | **Choose by what a missing partner means to you.** A customer with no orders is still a customer, so _From first list_ keeps them. An order with no customer record is a problem worth seeing, so _From both lists_ surfaces it. ## When both records use the same field name A matched pair can carry the same field twice, when both lists have a `name` with different values. **When field names collide** decides which one survives. Set it to **Second list wins**, the default, and the second list's value is kept; **First list wins** keeps the first's. The two match fields count as ordinary fields here: when they have different names, both stay in the merged record, and when they share a name this setting picks the value like any other field. ## Capital letters matter unless you say otherwise Matching is exact by default: `ACME` and `acme` are two different customers, and neither finds the other. **Ignore letter case** is off to begin with. Turn it on and they match. Codes that arrive from different systems are the usual reason: one exports upper case, the other lower. ## One record can match several Nothing stops a record in one list from matching more than one in the other. **Every match is returned, one row each.** One customer against two orders for the same `id` gives two rows, both carrying the customer's fields: ``` [{"id": 1, "name": "Lovelace", "total": 100}, {"id": 1, "name": "Lovelace", "total": 999}] ``` That is usually what you want. It also means the result can be longer than either input, which is worth knowing before you count rows downstream. ## What it passes on Combine returns an object with two fields: `results` holds the combined list and `resultCount` holds its size. ``` {{ 5.results }} ``` A step after it can iterate over that list, or read a single entry: ``` {{ 5.results.0.name }} ``` `{{ 5.resultCount }}` gives the number of records without counting them yourself. ## Limits Each input list can hold up to 10,000 records, and the combined result up to 100,000. A run over either ceiling fails with a message naming the list. Narrow each branch with [Filter](/build/action-steps/filter-items) before combining, or split the work across runs. ## Examples to copy ### Attach service levels to incidents Incidents arrive from a ticketing tool; the service level for each client lives in a Custom Variables step. - **How to combine**: Match fields - **Field in first list**: `clientId` - **Field in second list**: `clientId` - **Keep unmatched records**: From first list - **Ignore letter case**: on _From first list_ keeps an incident whose client has no service level recorded. Those are the ones worth noticing, and dropping them would hide the gap. Ignoring case covers clients whose id is written differently by the two systems. ### Put two exports one after the other Two branches each fetch a page of results, and you want them as one list. - **How to combine**: Append lists The first list's items come first, then the second list's. No field names are involved. ## What's Next? - [Filter](/build/action-steps/filter-items) — split one list into what you keep and what you discard - [Custom Variables](/build/action-steps/custom-variables) — hold a lookup table for matching against - [Variable Reference Syntax](/reference/variable-syntax) — how to read a list entry by position --- Source: https://docs.getglow.ai/build/action-steps/conditions # Conditions Step > Route a workflow down different paths by testing your data, using typed operators, plain-language AI rules, or both. The **Conditions** step decides where a workflow goes next. Each condition that is met sends the run down its own route, and an **ELSE** route catches the case where none are. > Dock: Flow · Returns: the route taken ## Every condition is checked on its own The most useful thing to know first: Conditions is not a single test with two outputs. You add as many conditions as you need, and **each is evaluated independently**. A condition that is met sends the run to the step you point it at. Two conditions can both be met, and both routes run. Conditions checks each route in turn; the ones that match run, and if none does, the Else route takes the run. **ELSE runs only when no condition is met.** It is the catch-all, not the "false" side of a pair. **Conditions is the one that can take more than one route.** Where the answer is one of several — a refund, a complaint or a question — [Switch](/build/action-steps/switch) is the step, and exactly one route runs. See [Choosing a Flow Step](/build/action-steps/routing) for the difference between the three routing steps. > **A step with no conditions set passes everything through.** The panel says so > in place: _"All data passes through if no conditions are set."_ An empty > Conditions step is not a closed gate. It is an open one. ## Two kinds of rule Every rule is one of two kinds, and you pick which when you choose its **type**. A new rule starts as an **AI Condition**, so describing the test in words is always available without setting anything up. | | Typed rule | AI Condition | | --------------------- | ----------------------------------------------------------- | ---------------------------------------------------------------------- | | **How you write it** | Pick a type, an operator and a value | Describe the test in plain language | | **How it decides** | A comparison | A model reads the value and judges | | **Same input, twice** | Same answer, always | Can differ between runs | | **Cost** | None | One model call each time it is evaluated | | **Good for** | `status is equal to active`, `amount is greater than 10000` | "the message sounds like a complaint", "the address is outside the UK" | **AI Condition is the one to reach for when the test needs judgement**: tone, intent, whether free text describes a complaint. Nothing else in the step can answer those. **Switch the type to a comparison once you know the exact value you are testing.** Six types cover the ordinary cases — Text, Number, Yes/no, Record, List, Date & Time — and a comparison costs nothing, answers instantly, and gives the same result every time. On a workflow running at volume, that difference adds up. You can mix the two in one step. A condition can hold a typed rule and an AI rule together, joined with AND or OR like any other pair. ## Setting it up ### Add a condition Click **Add new condition**. Each condition gets its own **Then go to** route. ### Pick the data to test Click the left-hand field to open the data picker. It lists **System Variables**, **Team Variables and Secrets**, and every step before this one, grouped by step number. If the step has already run, the picker shows its **real values** next to each field. You choose `email` while looking at an actual address. > **The picker's labels are shortened, and they are not the reference syntax.** > A field shown as `3.email` is the `email` inside step 3's output. The real > reference depends on the step: for an HTTP Request or app step it is > `{{ 3.ret.email }}`, and other steps use a different wrapper or none (see > [Variable Reference Syntax](/reference/variable-syntax)). Insert fields by > clicking them rather than typing what the label appears to say. ### Choose the type, then the operator Pick the kind of data you are testing: **Text**, **Number**, **Yes/no**, **Record**, **List**, or **Date & Time**. The operator list changes to match it. ### Set the value to compare against The right-hand field takes text or another dynamic reference. Operators like _is empty_ need no second value, and the field disappears when you pick one. ### Point the route at a step Under **Then go to**, choose where the run continues when this condition is met. Do the same under **ELSE** for the case where none are. ## Operators **Pick the type first.** The operators you get depend on it. Choose **Text** and you can ask whether text contains a word. Choose **Number** and you can ask whether a value is over 100. The list changes underneath you, so a type left on the default offers you the wrong questions. Start here: | Testing… | Choose | Then reach for | | --------------------------------- | ----------- | ---------------------------------------- | | Names, emails, statuses, any text | Text | `is equal to`, `contains`, `starts with` | | Amounts, counts, scores | Number | `is greater than`, `is less than` | | A yes/no flag | Yes/no | `is true`, `is false` | | A list, and how long it is | List | `contains`, `length greater than` | | A record with fields in it | Record | `exists`, `is empty` | | A date or timestamp | Date & Time | `is after`, `is before` | | Whether something is there at all | any type | `exists`, `is empty` | Those last four (`exists`, `does not exist`, `is empty`, `is not empty`) work whatever type you pick, and need no value to compare against. They are the ones to use when the question is "did we get this field?" rather than "what is in it?". > **Where a field may be absent, test that it is there first.** > > A field that never arrived is not equal to anything, so `is not equal to` > matches it. That is usually not what you meant: you wanted records whose value > differs, not records that have no value at all. > > Click **Rule**, put `exists` on the same field, and add the comparison as an > **AND** rule after it. The condition then covers only the records you meant. > > This applies to `is not equal to`. The other operators return no match for a > field that is absent, so they need no guard. The full lists, if you want them: `is equal to` · `is not equal to` · `contains` · `does not contain` · `starts with` · `does not start with` · `ends with` · `does not end with` · `matches regex` · `does not match regex` The last two take a **regular expression**, a pattern language for matching text. It is useful for things like "a valid-looking email" or "starts with three digits". If you have not met one before, `contains` and `starts with` cover most needs. A pattern that takes too long to evaluate fails the rule rather than holding up the run. `is equal to` · `is not equal to` · `is greater than` · `is less than` · `is greater than or equal to` · `is less than or equal to` An empty field does not count as zero. "Is the discount less than 100" is **no** when there is no discount at all, rather than yes. `is true` · `is false` · `is equal to` · `is not equal to` `is true` and `is false` need no value to compare against. > **Almost any text counts as true here, including the word `no`.** > > This type is for real yes/no flags. If your data carries the words `yes` > and `no`, or `Y` and `N`, choose **Text** and `is equal to` instead, or > everything comes out true. Only `exists`, `does not exist`, `is empty` and `is not empty`. You can ask whether a whole record is there, and nothing more. There is no way to compare two records against each other. To test something inside the record, point the condition at that field instead of at the record. `contains` · `does not contain` · `length equal to` · `length not equal to` · `length greater than` · `length less than` · `length greater than or equal to` · `length less than or equal to` `contains` asks whether an item is in the list. The `length` operators compare how many items it has. `is equal to` · `is not equal to` · `is after` · `is before` · `is after or equal to` · `is before or equal to` ISO dates, HTTP/RFC1123 dates and `YYYY-MM-DD HH:mm:ss` all parse. A numeric epoch (a plain count of seconds, like `1735689600`) does not, and neither does a time on its own like `10:30`. ## Combining rules with AND and OR A single condition can hold as many rules as it needs. Click **Rule** and choose which kind to add: | Choice | What it does | | ------------ | ----------------------------------------------------- | | **AND rule** | Adds to the current group. Every rule in it must hold | | **OR rule** | Starts a new group. Any one group matching is enough | There is no combinator to set. Rules inside a group are always joined with AND, and groups are always joined with OR — the choice you make is which of the two you are adding, and the panel prints **AND** and **OR** between the rows so you can read the result back. That covers the usual shapes. "Over £10,000 **and** in the UK" is two AND rules in one group. "Over £10,000 **or** flagged urgent" is two groups. > **A condition with no rules matches everything.** An empty group passes, so a > route left unconfigured fires on every run alongside whatever else matched. > Delete a route you are not using rather than leaving it blank. ## Writing an AI Condition Choose **AI Condition** as the rule's type and the operator menu is replaced by a single box: describe what you are testing, in words. **Prompt Examples** offers starting points. Write it as a statement that is either true or false of the value in front of it — "this message is asking for a refund" reads better than "check if refund". The model sees the value you pointed the rule at and nothing else, so anything it needs to judge has to be in that value. AI Condition is available in **Conditions** and [Filter](/build/action-steps/filter-items#describing-a-rule-in-words). The [Switch](/build/action-steps/switch) step is typed-only. In Filter it costs a call **per item**, so a hundred-item list is a hundred calls; in Conditions it is one call per rule, per evaluation. > **A rule the model could not answer counts as "not met".** If the call fails > or comes back unreadable, that route does not fire and the run carries on — > falling to ELSE if nothing else matched. The reason is recorded against the > rule in the run's history, so open the step in > [Executions](/build/core-concepts/executions) when a branch you expected did > not run. > > That is the safe behaviour for routing, and it is a reason to keep an AI rule > off the path where a missed branch would go unnoticed. ## What it passes on After a run, the step's badge tells you what happened: | Badge | Means | | -------------- | -------------------------------------------------------- | | **Green tick** | A condition was met and the run continued down its route | | **Red X** | No condition was met | A red X with an empty ELSE route means the run stopped there. That is a legitimate design, and it is how you end a branch deliberately. But it looks identical to a mistake, so set an ELSE route if you want the distinction to be visible later. ## Limits ### Set capitalisation for each text rule Text is compared exactly, so `Active` does not match `active`. To ignore capitals, turn on **Ignore capitalisation** on the rule itself. The setting applies to the rule where you turn it on. For a Conditions step with four text rules, set **Ignore capitalisation** on each rule that should match regardless of case. The toggle only appears where it changes the answer: on text comparisons and on a List `contains`. Numbers, dates and the presence checks have no capitals to ignore. > **With capitals ignored, a `matches regex` rule lowercases the value but not > your pattern.** An uppercase letter written into the pattern can then never > match. Write the pattern in lower case, or leave the toggle off and handle > capitals in the pattern itself. ## Examples to copy ### Route big leads Send enterprise-sized leads down a different path from everyone else. | Setting | Value | | -------------- | ---------------------------- | | **Data** | `{{ 2.ret.employee_count }}` | | **Type** | Number | | **Operator** | `is greater than` | | **Value** | `100` | | **Then go to** | the enterprise sequence | | **ELSE** | the self-serve sequence | Nothing here is "true" or "false". The condition either matches and takes its route, or it does not and ELSE takes over. ### Check a response before using it Continue only when an API actually returned something usable. | Setting | Value | | -------------- | -------------------------------------------------------------------------- | | **Data** | `{{ 3.ret.status }}` | | **Type** | Number | | **Operator** | `is equal to` | | **Value** | `200` | | **Then go to** | the step that reads the data | | **ELSE** | a [Stop and Error](/build/action-steps/stop-and-error) naming the endpoint | The status field exists only with **Include Full Response** turned on in the [HTTP Request](/build/action-steps/http-request) step. ### Skip internal and test addresses Two rules joined with **AND**. Click **Rule** to add the second. | | Data | Type | Operator | Value | | ---------- | ------------------- | ---- | ------------------- | ------------------ | | **Rule 1** | `{{ 2.ret.email }}` | Text | `does not contain` | `@yourcompany.com` | | **AND** | `{{ 2.ret.email }}` | Text | `does not end with` | `.test` | Both must hold for the route to fire. Point ELSE at a logging step so you can see what was skipped. ## What's Next? - Route to many named outputs with the [Switch Step](/build/action-steps/switch). - Keep or discard items in a list with [Filter](/build/action-steps/filter-items). --- Source: https://docs.getglow.ai/build/action-steps/custom-variables # Custom Variables > Hold a value under a name you choose, and read it back anywhere later in the same run. The **Custom Variables** step holds one or more values under names you choose, so later steps can read them back by name. > Dock: Data · Change · Takes: any value · Returns: each variable by name **Keyboard shortcut:** `t+v` ## When it is worth one Any step can already read any earlier step by number, so a variable is not how you move data forward. `{{ 3.email }}` does that on its own. Reach for one when the value is not already sitting in an earlier step's output. A running total you keep adding to, a flag you set once and check much later, or a starting value you want written down in one obvious place instead of repeated across four fields. ## Setting it up 1. Press `t+v`, or open **Tools → Data → Change** in the dock and select **Custom Variables**. 2. Under **Variables**, type a name in the **Name** column and its value in the **Value** column. Each variable takes one row, and the step opens with an empty row ready to fill. 3. Select **Add variable** for each further value. One step can define several variables at once, which keeps the canvas shorter than a step per value. The minus at the end of a row removes that variable. 4. The value can be typed in directly (`0`, `pending`), or picked from an earlier step. The picker sits on the value only: a name is always something you type. > **A variable lasts one run and no longer.** The next run starts with nothing > set, whatever the last one left behind. To keep a value between runs, put it > in a [workspace variable](/manage/workspace-settings/secrets-and-variables), a > spreadsheet row, or a record in your own system. > **Each Custom Variables step keeps its own values.** Two steps can both set a > `status`, and they do not overwrite each other: `{{ 3.status }}` and > `{{ 7.status }}` are two different values, read from two different steps. > Within one step, a name used on two rows keeps the later row's value. ## Types Each row carries a **type**, shown beside its value: **Text**, **Number**, **Yes/no**, **List** or **Record**. It is set for you as you type — write `["red", "amber", "green"]` and the row reads **List**; write `42` and it reads **Number**. ``` ["red", "amber", "green"] row reads List {"plan": "pro"} row reads Record 42 row reads Number true row reads Yes/no ``` A typed list is a real list downstream: point **Run for each item** at it and it runs once per entry, the same as a list from an earlier step. **A row holding a reference is Text.** `{{ 3.ret.items }}` on its own still arrives as whatever that step produced, with its type intact — the row shows Text because the value is a reference until the run resolves it, not because the list is flattened. Mix a reference into surrounding text and the result really is text. `Order {{ 3.id }} for {{ 3.customer }}` is one sentence, and that is all combining values this way can produce. ## What it passes on Each variable becomes a field on the step, named exactly as you named it. A step 3 that sets `discount_rate` is read downstream as: ``` {{ 3.discount_rate }} ``` There is no wrapper in between. The variable name follows the step number directly. Insert references from the data picker rather than typing them, and check the [Variable Reference Syntax](/reference/variable-syntax) if a path is not resolving. ## Examples to copy ### A decision made early, used late A workflow looks up a customer at step 2. It needs their loyalty tier at step 9, after a lookup, a branch and a loop have all run. 1. At step 3, set `loyalty_tier` from the CRM lookup. 2. At step 9, use `{{ 3.loyalty_tier }}` to pick the discount. You could reference the CRM step directly. The variable earns its place when the tier comes from somewhere different depending on the branch taken. Then one name means one thing, wherever the run went to find it. ### A flag decided mid-flow 1. Somewhere in the middle, a Custom Variables step (say step 7) sets `requires_approval` from a rule: an amount over the threshold makes it `true`. 2. At the end, branch on `{{ 7.requires_approval }}` to decide whether to route through [Human Review](/build/action-steps/user-approval). **Read the flag from the step that set it.** A variable belongs to its step, so a later step setting the same name does not change an earlier one. The branch has to reference the step whose value it wants. ### Counting what a loop did You do not need a variable for this. A step running per item passes on `{{ N.stats }}`, with `total`, `succeeded`, `failed` and `skipped` already counted. See [Loops & Iteration](/build/action-steps/loops). Reach for a variable only when you are counting something the loop does not track for you. ## What's Next? - Reference stored values correctly using the [Variable Reference Syntax](/reference/variable-syntax). - Store values that must persist across runs in [Secrets and Variables](/manage/workspace-settings/secrets-and-variables). --- Source: https://docs.getglow.ai/build/action-steps/date # Date > Format a date for people to read, shift it, pull out a part of it, measure the gap between two, or round it. The **Date** step does one thing to a date and hands back the answer. Format it for a person, shift it, pull out one part, measure the gap between two dates, round it, or take the current date. > Dock: Data · Change · Takes: a date · Returns: a date or a number Dates are unavoidable in operations work: invoice due dates, SLA windows, "anything older than 30 days". This step handles them without a trip to the Code editor. > **Format or shift dates directly inside fields:** You can format, shift, or snap dates directly within any variable reference using [Data Transformation](/build/core-concepts/data-transformation): > - Format for reading: `{{ 1.created_at | format_date:date_medium:Europe/Prague }}` > - Add 30 days: `{{ 1.invoice_date | date_add:30:days }}` > - Start of month: `{{ 1.timestamp | date_start_of:month }}` > > Use the dedicated **Date** canvas step when you need to calculate gaps between dates (Between) for downstream [Conditions](/build/action-steps/conditions) or when multiple steps reuse the computed date. ## Setting it up ### Add the step Open **Tools** in the dock and choose **Date**. ### Pick what to do **What to do** decides which of the other fields appear: | Operation | What it does | | ------------ | --------------------------------------------- | | **Format** | Renders a date as display text | | **Add** | Moves a date forwards | | **Subtract** | Moves a date backwards | | **Extract** | Pulls out one part: the year, month, weekday… | | **Between** | Measures the gap between two dates | | **Round** | Snaps to a whole day, hour, month… | | **Now** | Takes the current date and time | ### Give it the date **Date** takes what you are working with, usually a reference such as `{{ 1.created_at }}`. Write it out as `2026-08-03`, `2026-08-03 14:22` or `2026-08-03T14:22:00Z`, or use `{{ $now }}`. **Between** also needs **Second date**. ### Fill in the rest **Add** and **Subtract** take **How much** and a **Unit**: years through seconds. **Extract** takes **Part to extract**: year, quarter, month as a number or a name, week of the year, day of the month, day of the year, day of the week as a number or a name, hour, minute or second. **Between** takes **Measure in**. The gap is counted in whole units, so 36 hours measured in days answers 1. **Round** takes **Round to** (year, month, week, day, hour or minute) plus a **Direction**. Down goes back to the start of the unit; up goes forward to the next boundary. A date already sitting on a boundary is left alone either way, and weeks start on Monday. ### Choose a format and timezone **Format** offers ISO (`2026-08-03`), day-first (`03/08/2026`), month-first (`08/03/2026`), long date (`3 August 2026`), date and time (`2026-08-03 14:22`), long date and time (`3 August 2026 at 14:22`), or **Custom**. Custom takes a pattern built from these tokens: `YYYY` `YY` for the year, `MMMM` `MMM` `MM` `M` for the month, `DD` `D` for the day, `dddd` `ddd` for the weekday, `HH` `H` `hh` `h` for the hour, `mm` `m` for minutes, `ss` `s` for seconds, and `A` `a` for am/pm. Anything in `[square brackets]` is written out as-is, so `[Due] DD MMM` gives `Due 03 Aug`. **Timezone** matters whenever a date crosses midnight for somebody. Set it to the timezone the reader is in, not the one the server is in. > **If the incoming date is in an unusual shape**, use **Input format** to say > how to read it. Without it, Glow makes a sensible guess, which is right for > ISO dates and ambiguous for things like `03/04/2026`. ## What it passes on **Two keys mean the same thing on every operation, so learn them once:** | Reference | What it holds | | ------------------- | ---------------------------------------------- | | `{{ N.date }}` | The machine-readable instant, for another step | | `{{ N.formatted }}` | The display text, for a person to read | Use `formatted` in a message and `date` when another step needs to work with it. **`{{ N.result }}` is the operation's primary answer**, and what kind of thing it is depends on the operation: an instant from Format, a number from Extract or Between. `{{ N.resultType }}` names that kind (`date`, `text` or `number`), so one look tells you what you are about to reference. **Extract and Between have no instant to give**, so `date` and `formatted` are absent on those two rather than holding something misleading. A reference to a key that is not there fails visibly instead of returning a plausible wrong value. | Operation | `result` is | `date` and `formatted` | | --------------------------------- | ----------- | ---------------------- | | Format, Add, Subtract, Round, Now | an instant | present | | Extract | a number | absent | | Between | a number | absent | The step also publishes the **operation** and **timezone** it used, so a stored run can be read back later without opening the panel. ## Examples to copy ### A due date thirty days out **What to do:** Add · **Date:** `{{ 1.invoice_date }}` · **How much:** 30 · **Unit:** days. Put `{{ N.formatted }}` in the email and `{{ N.date }}` wherever another step needs the date itself. ### How old is this ticket? **What to do:** Between · **Date:** `{{ 2.created_at }}` · **Second date:** `{{ $now }}` · **Measure in:** days. Follow it with a [Condition](/build/action-steps/conditions) on `{{ N.result }}` to escalate anything past your SLA. ### Only run on weekdays **What to do:** Extract · **Part to extract:** Day of the week (number). Days are numbered the international way: **Monday is 1 and Sunday is 7**. A [Condition](/build/action-steps/conditions) on `{{ N.result }}` set to **is less than** `6` therefore runs on weekdays only. ### The start of this month **What to do:** Round · **Round to:** month · **Direction:** down. Useful as the "from" date of a monthly report. ## What's Next? - Branch on the answer with [Conditions](/build/action-steps/conditions). - See what `{{ $now }}` and `{{ $today }}` give you in [Variable Syntax](/reference/variable-syntax). --- Source: https://docs.getglow.ai/build/action-steps/delay # Wait > Pause a run for a set time, until a date and time, or until a webhook call or form submission resumes it. The **Wait** step pauses a workflow and decides when it starts again. It can wait a fixed amount of time, wait until a particular date and time, or hold the run open until something outside Glow answers. > Dock: Flow **Find it under Flow in the Tools panel**, or place it with the `f` then `d` shortcut. ```mermaid flowchart LR A[Create User] --> B[Wait 10 Minutes] --> C[Send Welcome Email] ``` A waiting run is not running. Glow puts the run down and picks it up when the wait is over. A step that waits two days costs no more than one that waits two seconds. ## Setting it up The first field is **Wait Mode**, and it changes every field below it. The four modes are: | Wait Mode | Continues when | | ---------------------------------- | ---------------------------------------------- | | **For a time interval** | The amount of time you set has passed | | **Until a specific date and time** | The clock reaches the date and time you set | | **On webhook call** | Another system calls this run's resume address | | **On form submitted** | Somebody fills in a form and submits it | The default is **For a time interval**. ### For a time interval Two fields: **Amount** and **Unit**. Amount defaults to 5, Unit offers Seconds, Minutes, Hours and Days. "Wait 2 days" is Amount 2, Unit Days. Reach for this mode when you are spacing out calls to another service. It also suits giving a system a moment before you read back something you just wrote. ### Until a specific date and time Two fields: **Resume at** and **Timezone**. **Resume at** takes a date and time like `2026-01-01 09:00`. It also takes a reference to an earlier step, which is where most of its value is. A renewal date that arrived in the trigger becomes the moment the run continues: ``` {{ 2.renewalDate }} ``` **Timezone** is the zone that date and time is read in. It defaults to the timezone of whoever built the workflow. Daylight saving is applied for the date being waited for rather than for today. A wait set now for a date in June gets June's offset. A value carrying its own offset (ending in `Z`, or `+02:00`) is read as an absolute moment. The Timezone field does not apply to it. If the moment you name has already passed when the step runs, the workflow carries straight on rather than failing. "Wait until 09:00" evaluated at 09:05 continues immediately. ### On webhook call The run stops and waits for another system to call it. When the step runs, Glow mints a resume address that belongs to that one run, and publishes it on the step's output as `resumeUrl`. Send it to whoever needs to call it back: ``` {{ 4.resumeUrl }} ``` Put a step after the Wait that hands the address on. Two common shapes: an HTTP Request that registers it as a callback with a supplier's API, or a Slack message that posts it into a channel. Anything the caller POSTs to that address is carried into the workflow as `formData`. A supplier confirming with `{"orderId": "A-91", "status": "shipped"}` gives your later steps `{{ 4.formData.orderId }}` and `{{ 4.formData.status }}`. The address is created when the step runs, not when you build the workflow, and each one answers for a single run. There is no address to copy out of the panel beforehand. Calling an address a second time, after the run has moved on, does nothing. > **Run the workflow once and confirm it is waiting before you build on it.** > Open the step's **Executions** tab after the run: a suspended step shows a > pending status and its resume address, and the steps after it have not fired. > That check takes a moment and tells you the address is live and reachable > before you hand it to a supplier or post it into a channel. ### On form submitted The same suspension, but the address opens a page a person fills in. Configure the page with: | Field | What it does | | -------------------- | ------------------------------------------------------ | | **Form Title** | Heading shown above the form | | **Form Description** | Short explanation shown under the heading | | **Form Fields** | The fields somebody fills in. At least one is required | Each field takes a **Field name**, which is the key later steps read it by, and a **Label**, which is what the person sees. You also set a **Type**, whether it is **Required**, and optional **Placeholder** and **Help text**. The choice types take a comma-separated list of **Choices**. The types available are short text, long text, number, date, dropdown, multi-select, checkbox, radio and file. The form page shows the workflow's name, your title and description, and the deadline if you set one. Once somebody submits, the page says so. A second visitor to the same link sees that it has already been answered. The answers arrive as `formData`, keyed by the field names you chose. A field named `reason` is read by a later step as `{{ 4.formData.reason }}`. > Both waiting modes resume through a published workflow. Where the workflow is > no longer Live, the address reports that it needs publishing rather than > continuing the run. ### Resumed and Expired The two waiting modes give the step two outputs, listed under **Branches**: **Resumed** and **Expired**. Drag a connection from each to the steps that should follow it. Resumed is the normal path, taken when the call or the submission arrives. Expired is taken when nothing arrives in time. Leave Branches empty and the step behaves like any single-output step. Resuming continues down its normal output, and expiring settles the run through its error path. An unanswered wait is therefore visible as a failure rather than mistaken for an answer. Wiring the Expired branch turns that into a path of its own, such as a chaser email or a hand-off to a person. **Timeout (seconds)** is what sets the deadline. Leave it blank or at 0 and the step waits indefinitely, and only Resumed can ever fire. ## What it passes on What the step publishes depends on its mode. **For a time interval and Until a specific date and time**, the step publishes one value: | Reference | Contains | | ---------------- | ------------------------------------------------------------------------------- | | `{{ N.result }}` | A sentence confirming the wait, e.g. "Next steps will be executed after 2 days" | The two timed modes wait and then carry on, so there is nothing else worth passing down. If a later step needs the moment the run resumed, use `{{ $now }}` there rather than reading it back off this step. **On webhook call and On form submitted**, while the run is paused: ```json { "status": "pending", "waitMode": "form", "resumeUrl": "https://…", "formUrl": "https://…", "expiresAt": "2026-08-10T09:00:00.000Z" } ``` `formUrl` is the page a person opens, and appears in form mode only. `expiresAt` is null when there is no timeout. Once the wait resumes, the step's own result carries what came back: | Reference | Contains | | ------------------------- | ------------------------------------------- | | `{{ N.formData. }}` | What was submitted, or what the caller sent | | `{{ N.decision }}` | `resumed` or `expired` | | `{{ N.answeredAt }}` | When the answer arrived | Steps before the Wait are untouched by it. Anything later steps need from them is read as usual. ## Limits A timeout, and a timed wait, both run to a maximum of **13 days**. The field refuses a larger number when you type it. To pause for longer, end the workflow and start a second one on a [Scheduler](/build/triggers/scheduler) trigger at the date you want. For anything longer, run the later half of the work as its own workflow on a [Scheduler Trigger](/build/triggers/scheduler) rather than holding a run open. That is also the better shape for "every morning at nine", which is a schedule rather than a pause inside one run. ## Examples to copy ### Stay under a third party's rate limit Put a Wait of Amount 2, Unit Seconds as the last step of a [Repeater](/build/action-steps/loops) loop body, before the connection back into the Repeater step. Each pass then waits before the next begins, so the requests arrive spread out rather than all at once. ### Continue on the renewal date Set Wait Mode to **Until a specific date and time** and Resume at to `{{ 1.renewalDate }}`. The run continues on the day named in the record that started it, with no scheduled job to maintain. ### Wait for a supplier to confirm Set Wait Mode to **On webhook call** and Timeout to `172800` for two days. Follow the Wait with an HTTP Request that sends `{{ 4.resumeUrl }}` to the supplier as a callback address. Wire Resumed to the fulfilment steps and Expired to a chaser email. ### Ask somebody for the missing detail Set Wait Mode to **On form submitted** and add a short text field named `poNumber`. Email `{{ 4.formUrl }}` to the account manager. Later steps read `{{ 4.formData.poNumber }}`. > Because Glow is multiplayer, a teammate watching a running workflow sees when > a Wait step started and when it is due to resume. ## What's Next? - Run a workflow on a recurring clock instead of pausing mid-run with the [Scheduler Trigger](/build/triggers/scheduler). - Ask a named person for a decision, with buttons and branches of your own, using [Human Review](/build/action-steps/user-approval). - See execution ceilings in [System Limits & Quotas](/reference/system-limits). --- Source: https://docs.getglow.ai/build/action-steps/filter-items # Filter > Check every item in a list on its own and split it into the ones you keep and the ones you discard. The **Filter** step takes a list and checks each item on its own. Items that match your rules continue on the **KEPT** route, and the rest go to **DISCARDED**. > Dock: Flow · Takes: a list · Returns: two lists Use it to strip a list down before doing expensive work on it. You might keep only active users out of a hundred, or only the orders above a threshold. **Think of it as a Condition applied to every item**, with the verdict's routing built in: an item that passes continues on KEPT, one that fails takes DISCARDED. Each route carries on with its share of the list — and a route that ends up with no items does not run at all. **Filter is the only one that divides a list.** The other two look at the run as a whole and pick a path. Ask yourself whether you are splitting _items_ or choosing a _route_ — and see [Choosing a Flow Step](/build/action-steps/routing) for the difference between the three. Filter splits a list in two: the items that matched leave by one route and the rest by Discarded, and both routes run. ## Choose the list, then write the rules The panel opens with **List**: choose which list to filter, from the steps connected before this one. Connect an earlier step first and its lists are offered; **Enter it myself** takes a typed list instead. Then each rule reads the way you would say it out loud: **a property of an item, an operator, a value**, as in `status is equal to active`. The property picker offers the fields of the list you chose. Before an earlier step is connected, the List field says so: > Connect an earlier step to choose one of its lists. > **Connect the step that returns the list first.** The picker only offers lists > from steps before this one on the canvas. Until the link exists, the Filter > has nothing to offer you and the FILTERING line stays empty. To point the Filter at a different list, pick a property from that list in one of the rules. The binding follows the property. Check the FILTERING line afterwards to confirm you landed on the list you meant. That matters when two upstream steps return similar-looking lists. ## Setting it up ### Select a property Click **Select a property** on the first rule and choose a field of the item you want to test. ### Choose the type and operator Pick the data type: **Text**, **Number**, **Yes/no**, **Record**, **List**, or **Date & Time**. Then pick an operator from that type's list. The catalogue is shared with [Conditions](/build/action-steps/conditions#operators). ### Add more rules if you need them Click **Rule** to add another. Rules within a group are joined with **AND**. Separate groups are joined with **OR**. The panel states the result plainly: items matching **all rules in any group** are kept. ### Point the routes at steps Choose a step under **KEPT**. Under **DISCARDED**, choose a step if you want to do something with the rejects, or leave it unassigned to drop them. > **With no rules configured, every item is kept.** An unfinished Filter is not > a closed gate. ## Referring to the item Inside the rules, `{{ $item }}` is the whole item, `{{ $item.field }}` one of its properties, and `{{ $itemIndex }}` its position in the list, counting from 0. > Filter uses `$item`, with a dollar sign. [Run for each > item](/build/action-steps/loops/run-for-each-item) uses `item`, without one. > The two are separate mechanisms and the tokens are not interchangeable. ## One record works too Filter is happiest with a list, but a **single record** is fine: it is treated as a list of one, and comes out on KEPT or DISCARDED like any other item. That makes it a useful gate for a single thing. "Only carry on if this order is over £500" is a Filter with one record in and one route out, and unlike a [Conditions](/build/action-steps/conditions) step, the item itself travels down the route rather than only the decision. What the step accepts: | Input | What happens | | ----------------------------------------- | ------------------------------- | | A list | Each item is checked on its own | | A single record | Treated as a list of one | | Text holding a list or record | Read as one, if properly formed | | A blank value, a bare number or some text | **The step fails** | The last row is deliberate. A bare value has no fields to test, so wrapping it silently would produce a rule that can never match. The step says so instead: _"Filter needs a list or record. Connect a step that returns a list, or pick a field that holds one."_ ## When an item does not make sense Sometimes a rule cannot be answered for a particular item. You are comparing dates and one record has "last Tuesday" in that field, or comparing numbers and one has "N/A". **Those items are discarded rather than stopping the run.** One bad record does not cost you the other ninety-nine, and the step tells you it finished with problems. The catch is that **discarded then means two things at once**: items that genuinely did not match, and items nobody could make sense of. If that distinction matters, send DISCARDED somewhere you can look at it rather than dropping it. ## Capital letters matter here **Ignore capitalisation** is off by default, so `Active` does not match `active`. Text is compared exactly until you say otherwise, which is the same rule the [Switch](/build/action-steps/switch) step follows. Turn it on to ignore capitals across every rule in the step. You can also set it on **a single rule**, which overrides the step for that rule alone. That helps when one field arrives from a system that capitalises inconsistently and the rest do not. ## Describing a rule in words Where no operator fits, set a rule's type to **AI Condition** and write what you are testing in plain language: "the message sounds like a complaint", "the address is outside the UK". **Prompt Examples** offers starting points. > **Watch the count on a long list.** An AI condition costs a model call for > every item, so a hundred items means a hundred calls, and two runs on the same > item can differ. Use it for the tests a comparison cannot express, and switch > the rest to a typed operator once you know the value you are testing. AI Condition is available in **Filter** and [Conditions](/build/action-steps/conditions#writing-an-ai-condition). The [Switch](/build/action-steps/switch) step is typed-only. ## A branch with no items does not run If every item matches, nothing connected to DISCARDED fires. If none match, nothing connected to KEPT fires. That is usually what you want, but it means a step on only one route is skipped when that route has no items. The run finishes successfully, without doing that work. If something must happen either way, put it before the Filter or on both routes. ## What it passes on Downstream steps read the results by number, like any other step: | Reference | What it holds | | ------------------------ | ----------------------- | | `{{ N.kept }}` | The items that matched | | `{{ N.discarded }}` | The items that did not | | `{{ N.keptCount }}` | How many were kept | | `{{ N.discardedCount }}` | How many were discarded | > **Discarded items are always in the step's result, even with the DISCARDED > route unassigned.** Leaving that route empty stops them continuing through the > workflow. It does not throw them away. If you need to see what fell out, read > `{{ N.discarded }}` on any later step. ## Limits A single Filter step processes up to **100,000 items**. Above that it stops with an error rather than quietly doing part of the job. If you use `matches regex` in a rule, all that pattern-matching together gets **five seconds** across the whole list. Any single pattern gets **250 milliseconds**. Go over either and the step fails, naming how many items it got through. It will not hand you a half-filtered list. If you hit it, filter on something simpler first to cut the list down, or simplify the pattern. ## Examples to copy ### Keep only active users | Setting | Value | | ------------ | -------------------------------- | | **Property** | `status`, from the list of users | | **Type** | Text | | **Operator** | `is equal to` | | **Value** | `active` | Picking `status` out of the list of users is what binds the Filter to that list. Check the FILTERING line to confirm. The next step reads `{{ N.kept }}`. ### Drop consumer email domains Two rules with **AND**. Click **Rule** to add the second. | | Property | Type | Operator | Value | | ---------- | -------- | ---- | ------------------- | ------------ | | **Rule 1** | `email` | Text | `does not end with` | `@gmail.com` | | **AND** | `email` | Text | `does not end with` | `@yahoo.com` | Both must hold for an item to be kept. Matching is exact by default, so turn **Ignore capitalisation** on if `@Gmail.com` should be caught too. ### Keep products that are in stock | Setting | Value | | ------------ | ----------------- | | **Property** | `stock_count` | | **Type** | Number | | **Operator** | `is greater than` | | **Value** | `0` | Point **DISCARDED** at a step that logs what fell out rather than leaving it empty. Otherwise you cannot tell "nothing was out of stock" from "the rule was wrong". ## What's Next? - Process each kept item individually with [Loops & Iteration](/build/action-steps/loops). - Route the whole run instead of a list with the [Conditions Step](/build/action-steps/conditions). --- Source: https://docs.getglow.ai/build/action-steps/helper-functions # Helper Functions > Ready-made utility steps for formatting, conversion, and lookups, without writing a Code editor step. **Helper Functions** is an app of small utility steps for everyday data work. It covers converting values, reshaping text, and similar chores that would otherwise need custom code. Find it in the dock under **Tools → Data → Convert**, alongside **Formatting** and the built-in data steps. > Helper Functions are **steps you place on the canvas**, not expressions you > type into a field. There is no `{{ formatDate(...) }}` function syntax. > Placeholders reference data, they do not call functions. See [Variable > Reference Syntax](/reference/variable-syntax) for what a placeholder can > contain. ## Setting it up ### Open the dock Click **Tools** in the dock, then the **Data** category. ### Pick a function Open **Helper Functions** and choose the operation you need. Each one is a normal step with its own inputs and output. ### Wire it up Connect it into your flow. Reference its output downstream by step number, for example `{{ 3.result }}`. Some functions do not appear in the catalogue, because a native step does the same job better. These are the ones you are most likely to go hunting for: | Looking for | Use instead | | ----------------------- | ------------------------------------------------------------------------------------------------------------ | | Send HTTP request | [HTTP Request](/build/action-steps/http-request) | | Schedule task in future | [Wait](/build/action-steps/delay), or the [Scheduler Trigger](/build/triggers/scheduler) for a recurring job | | Pause between steps | [Wait](/build/action-steps/delay) | | Trigger workflow | The [Subflow](/build/action-steps/trigger-workflow) step, which waits for the result | | Send email | The email connector for your provider, from the Apps panel | | Send to S3 | The AWS connector from the Apps panel | If a function you expected is not in the list, check **Common Alternatives** below before assuming it is missing. ## Formatting **Formatting** sits beside Helper Functions under **Tools → Data → Convert** and works the same way. Pick an action, and it becomes a step. It is where most everyday tidying lives. | Group | What it covers | | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | **Text** | Trim whitespace, transform case, replace text, set a default when a value is empty, encode or decode a URL, convert between HTML and Markdown | | **Extract** | Pull an email address, phone number, URL or number out of a longer string, or match your own regular expression | | **Date/Time** | Format a date, add or subtract time, compare two dates | | **Numbers** | Format a number, format a currency value | | **Data** | Convert JSON to a string | Reach for **Set Default Value** more often than seems necessary. An empty field arriving at a later step is one of the quieter ways a run goes wrong, and it costs one step to rule out. ## Common alternatives Much of what you might reach for a helper function to do is handled by a dedicated step: | You want to | Use | | ---------------------------------- | --------------------------------------------------------------------------------- | | Reshape or reformat data | [AI Data Transform](/build/ai-features/ai-transform) | | Parse a JSON string | [Parse JSON](/build/action-steps/parse-json) | | Break text into a list | [Split Text](/build/action-steps/split-text) | | Strip HTML down to plain text | [HTML to Text](/build/action-steps/html-to-text) | | Insert the current date or time | `{{ $now }}` / `{{ $today }}` — see [Variable Syntax](/reference/variable-syntax) | | Format, shift or compare a date | [Date](/build/action-steps/date) | | Group a list and count or total it | [Summarize](/build/action-steps/summarize) | | Anything not covered above | [Code editor](/build/action-steps/code-execution) | For a one-off transformation, **AI Data Transform** is usually the quickest route. Describe the change in plain language rather than finding the right function. For anything repeated, precise, or performance-sensitive, **Code editor** is the more predictable choice. It runs the same way every time, and is far easier to debug than a chain of small utility steps. ## What's Next? - Describe a transformation instead of coding it with [AI Data Transform](/build/ai-features/ai-transform). - Write it yourself in [Code editor](/build/action-steps/code-execution). --- Source: https://docs.getglow.ai/build/action-steps/html-to-text # HTML to Text > Strip the markup out of an HTML string and get readable text back. The **HTML to text** step takes a string full of markup and gives you back the readable text inside it. > Dock: Data · Convert · Takes: HTML · Returns: text Most often you need it because an email body, a CRM note or a fetched web page arrived as HTML. The next step wants text a person can read: a Slack message, a spreadsheet cell, a summary. > **No extra step needed?** You can strip HTML directly inside any field token using [Data Transformation](/build/core-concepts/data-transformation) with `{{ 2.html_body | strip_html }}`. Use this dedicated **HTML to text** step when you want the cleaned text stored as a standalone canvas output for multiple subsequent steps to reuse. **Keyboard shortcut:** `t+h` ## Setting it up 1. Press `t+h`, or open **Tools → Data → Convert** in the dock and select **HTML to text**. 2. In the **Input** field, pick the HTML from an earlier step. 3. Save the step. ## What it passes on The converted text is read by step number, like any other step: ``` {{ 3.ret }} ``` Run the step once and read the **Executions** tab to confirm the path before you build against it. That is quicker than reasoning it out, and it settles the question for good. ## What it leaves behind - **Styles and scripts.** Markup that is not readable text is stripped along with the tags. - **Entities and odd markup.** Encoded characters are turned back into the characters they stand for, and imperfectly nested tags do not stop the step. - **Empty input.** An empty or missing input produces an empty string. The exact output for a given page is worth confirming once in the **Executions** tab rather than predicting it. HTML in the wild varies more than any rule about it does. > **Links come through as text only.** "Visit this link" survives; the address > behind it does not. If you need the URLs, pull them out with a [Code > editor](/build/action-steps/code-execution) step before stripping the tags. ## Examples to copy ### What goes in and what comes out ```html
Thank you for your order, Jane.
Visit this link to track your order.
``` **Out:** ``` Order Confirmation Thank you for your order, Jane. - Item: Widget Pro - Quantity: 3 - Total: $89.97 Visit this link to track your order. ``` The step preserves the structure (paragraphs become line breaks, list items are prefixed with dashes) while removing all HTML markup. ### An email body into Slack An email trigger gives you the message as HTML. Slack shows the tags rather than the words unless you convert it first. **What to do:** add HTML to text after the trigger, set **HTML** to the email's body field, and put `{{ 3.ret }}` in the Slack step's message — replacing `3` with this step's own number. ### A page you fetched into an AI step An [HTTP Request](/build/action-steps/http-request) returns a page as markup, and an AI step reading it spends most of its budget on tags. **What to do:** put HTML to text between them, with **HTML** set to `{{ 3.ret.body }}`, and reference the converted text in the prompt. ## What's Next? - Break the resulting text into a list with [Split Text](/build/action-steps/split-text). - Write a custom parser for more complex extraction using [Code editor](/build/action-steps/code-execution). --- Source: https://docs.getglow.ai/build/action-steps/http-request # HTTP Request > Call any REST API from a workflow, with control over the method, URL, headers and payload. The **HTTP Request** step calls an API that has no ready-made step in Glow. It sends exactly the method, URL, headers and payload you configure, and nothing else. > Dock: Data · Dev tools · Returns: the API's response Reach for it whenever a service you need has no Glow connector yet, or when the connector exists but the endpoint you want is not one of its actions. It also reaches your own systems, provided they are reachable from the internet: an endpoint on a private network or a `localhost` address is refused. **Add it from Tools → Data → Dev tools on the canvas dock.** Searching the Apps panel for "HTTP" turns up a separate connector of the same name. This page describes the built-in step under Tools, which is the one to use. --- ## How it fits in a workflow ```mermaid flowchart LR A[Webhook Trigger] --> B[Parse JSON] --> C[HTTP Request] --> D{Status 200?} D -->|Yes| E[Slack Success] D -->|No| F[Slack Error Alert] ``` --- ## Setting it up ### Select the Method Choose the HTTP method required by the target API: - **GET:** Retrieve data from an API - **POST:** Create a new resource or submit data - **PUT / PATCH:** Update an existing resource - **DELETE:** Remove a resource - **HEAD / OPTIONS:** Inspect headers or verify CORS capabilities without downloading the body ### Define the URL Enter the full URL for the API endpoint (e.g., `https://api.example.com/v2/orders`). Inject values from earlier steps with `{{ }}`, referencing each step by the number shown on the canvas. For a field the trigger received, that gives you `https://api.example.com/orders/{{ 1.order_id }}`. Use the data selector beside the field rather than typing the path. What a step exposes varies by step type, and picking the value inserts the reference that actually resolves. **Query parameters have their own section.** You do not have to build a query string by hand. Add them as name/value pairs (or raw JSON) and Glow assembles the URL, handling URL encoding and nested keys (e.g., `filter[status]=active` or `ids[]=1&ids[]=2`) for you. ### Set Headers & Authentication **Authentication** is its own field, above the headers. Pick the type and fill in the value. Glow builds the header for you: | Type | What you supply | | ---- | --------------- | | **None** | Nothing. The default. | | **Bearer Token** | The token, e.g. `{{ $secret.API_TOKEN }}` | | **Basic Auth** | Username and password | | **API Key in Header** | The header name the service expects, and the key | Use **Headers** for everything else the API asks for: `Accept`, `Content-Type`, a request id. You only need to write an `Authorization` header by hand for a scheme none of the four types covers. Both **Headers** and **Query Parameters** can be filled in as name/value **Fields** or pasted wholesale as **JSON**. Use whichever matches what the API's own documentation gives you. > **Put credentials in [secrets](/manage/workspace-settings/secrets-and-variables), not in the header value.** A token typed directly into the field is visible to anyone who can open the workflow. It also travels with the workflow if that is shared or published as a template. ### Configure the Payload For methods that send data (POST, PUT, PATCH) configure the request body. **Body Type** offers: | Option | Sends | | --------------------- | ------------------------------------------------------------ | | **JSON** | `application/json` — the usual choice for REST APIs | | **Form URL Encoded** | `application/x-www-form-urlencoded` (`a=1&b=2`) | | **Form Data** | `multipart/form-data` for structured key-value form fields | | **Text** | `text/plain`, sent exactly as you write it | | **None** | No body at all (sends `Content-Length: 0` for POST/PUT) | **Form URL Encoded and Form Data are not the same thing.** If an API's documentation shows `a=1&b=2`, you want Form URL Encoded. ### Advanced Options & Timeout Configure behavior for slow or custom endpoints under **Advanced Options**: - **Timeout (seconds, 1-300):** How long the step waits before aborting. Defaults to **30 seconds**. - **Ignore HTTP Status Errors (4xx/5xx):** Treats non-2xx error codes as successful runs, passing the error payload downstream instead of stopping the run. - **Ignore SSL Certificate Issues:** Disables strict TLS/SSL certificate validation. Useful when connecting to internal staging servers, self-signed certificates, or testing environments. - **Include Full Response (status, headers, body):** Returns response headers and status codes alongside the body. > **Payload size limit:** Outbound responses are capped at **10 MB**. If an API returns a larger payload, the step halts with `OutboundResponseTooLargeError`. ### Skip the form: Import cURL Most API documentation provides a `curl` command. Click **Import cURL**, paste the command, and Glow automatically configures the method, URL, query parameters, headers, authentication, and request body.  *Paste the command as the API's documentation gives it to you. Glow reads the method, URL, headers and body out of it.* #### Examples to Copy & Import **1. POST Request with JSON Body and Bearer Auth:** ```bash curl -X POST https://api.getglow.ai/v1/leads \ -H "Authorization: Bearer {{ $secret.API_KEY }}" \ -H "Content-Type: application/json" \ -H "Accept: application/json" \ -d '{ "email": "lead@email.com", "name": "John Doe" }' ``` **2. GET Request with Query Parameters and Custom Header:** ```bash curl -X GET "https://api.getglow.ai/v1/customers?status=active&limit=50" \ -H "X-API-Key: {{ $secret.SERVICE_KEY }}" \ -H "Accept: application/json" ``` > Importing a cURL command overwrites whatever is currently configured on the > step. Import the cURL snippet first, then map any dynamic `{{}}` variables. --- ## What it passes on The response is stored under `ret`. Glow automatically detects the response content type: ### Default JSON Output Parsed JSON sits directly under `ret`: ```json { "ret": { "id": 1042, "title": "Invoice #1042", "status": "paid" } } ``` Access fields directly: `{{ 3.ret.id }}` or `{{ 3.ret.status }}`. ### Full Response (Status & Headers) When **Include Full Response** is enabled under Advanced Options, `ret` contains `body`, `headers`, and HTTP status codes: ```json { "ret": { "body": { "id": 1042, "title": "Invoice #1042" }, "status": 200, "statusCode": 200, "statusMessage": "OK", "headers": { "content-type": "application/json; charset=utf-8", "x-ratelimit-remaining": "98" } } } ``` Access fields with `{{ 3.ret.body.id }}` and status with `{{ 3.ret.status }}`. ### Binary & Non-Text Responses When downloading non-text files (PDFs, images, zip files), Glow encodes the binary buffer as Base64: ```json { "ret": { "data": "JVBERi0xLjQKJcfs...", "encoding": "base64", "contentType": "application/pdf", "fileName": "statement.pdf" } } ``` This object can be handed directly to file upload steps or forwarded to external APIs. > **Run the step once and read the Executions tab** rather than working the path > out from the API's own documentation. What Glow stores is the shape you > reference, and one look settles it. --- ## Troubleshooting & Error Handling > **When the API answers with an error** > > Any non-2xx status, such as `400 Bad Request` or `500 Internal Server Error`, is treated as a failure and halts the workflow. Three settings change that: > > - **Ignore HTTP Status Errors (4xx/5xx)**, in the step's own **Advanced Options**, turns an error status into an ordinary result. The step passes on the response body instead of failing. Use it when a `404` is a real answer ("no such customer") rather than a fault. Pair it with **Include Full Response** so a [Condition](/build/action-steps/conditions) can read the status and decide what it meant. > > The remaining two are in the step's **Test & Debug** tab: > > - **Retry on fail** repeats the request before giving up. Off by default, so a `429` or `5xx` fails the run on first response unless you turn it on. See [Error Handling](/build/core-concepts/error-handling). > - **If this step fails → Continue** lets the run carry on so you can handle the failure yourself. Follow it with a [Conditions](/build/action-steps/conditions) step checking `{{ 3.ret.status }}`, replacing `3` with this step's number. That reference needs **Include Full Response** turned on. Without it the step returns only the body and there is no status to test. ### Common Errors | Code | Meaning | What to Do | | :------ | :---------------- | :---------------------------------------------------------------------------------------- | | **401** | Unauthorized | Verify your authentication credentials in the headers. | | **403** | Forbidden | Confirm you have permission to access the requested resource. | | **404** | Not Found | Check the URL structure and verify any dynamically injected variables (like `id`). | | **429** | Too Many Requests | You have hit an API rate limit. Add a **Wait** step before the request to throttle calls. | ## Top Real-World Recipes ### Post Lead to Custom API Send clean dynamic data from an earlier step (e.g. step 1) to an internal endpoint: - **Method:** `POST` - **URL:** `https://api.yourcompany.com/v1/leads` - **Auth:** `Bearer Token` → `{{ $secret.CRM_API_KEY }}` - **Body (JSON):** ```json { "email": "{{ 1.email }}", "first_name": "{{ 1.first_name }}", "company": "{{ 1.company_name }}", "source": "glow_workflow" } ``` ### Call an API for Each Item in a List When running in **Run for each item** mode (or inside a [Repeater](/build/action-steps/loops/repeater)), reference the current item using `{{ item }}`: - **Method:** `PATCH` - **URL:** `https://api.example.com/v2/contacts/{{ item.id }}` - **Body (JSON):** ```json { "status": "synchronized", "last_updated": "{{ $now }}" } ``` Every row in the incoming list executes an isolated HTTP call. ### Forward Entire Webhook Payload To forward an entire incoming event without picking individual fields, use `$full_result`: - **Method:** `POST` - **URL:** `https://webhook.site/your-endpoint` - **Body (JSON):** ```json { "event_type": "typeform_submission", "timestamp": "{{ $now }}", "raw_payload": "{{ 1.$full_result }}" } ``` ### Fetch OAuth 2.0 Access Token Authenticate using `client_credentials` with **Form URL Encoded** body: - **Method:** `POST` - **URL:** `https://auth.provider.com/oauth/token` - **Body Type:** `Form URL Encoded` - **Fields:** - `grant_type`: `client_credentials` - `client_id`: `{{ $secret.CLIENT_ID }}` - `client_secret`: `{{ $secret.CLIENT_SECRET }}` The response token will be available in downstream steps as `{{ 2.ret.access_token }}`. --- ## What's Next? - **[Parse JSON](/build/action-steps/parse-json)**: Turn raw text or nested strings into addressable fields. - **[Error Handling & Retries](/build/core-concepts/error-handling)**: Configure automatic retries on 429/5xx status codes. - **[Secrets & Variables](/manage/workspace-settings/secrets-and-variables)**: Securely manage API tokens and environment endpoints. --- Source: https://docs.getglow.ai/build/action-steps/limit # Limit > Cut a list down to the first few entries. The **Limit** step shortens a list. You say how many entries to keep, and it returns that many from the start. > Dock: Data · Lists · Takes: a list · Returns: a shorter list Use it to stop work getting out of hand. The top ten results rather than every match. A handful of records while you are still testing. A batch small enough to stay inside an API's rate limit. > **Slicing inside a field?** You can take the first *N* items of a list or shorten a string directly inside field tokens using [Data Transformation](/build/core-concepts/data-transformation): > - First *N* list items: `{{ 2.items | keep_first:3 }}` > - First / last item only: `{{ 2.items | first }}` or `{{ 2.items | last }}` > - Shorten string length: `{{ 2.description | limit:100 }}` > > Use the **Limit** canvas step when passing a sliced list into a downstream [Loop](/build/action-steps/loops) or multi-step branch. ## Setting it up ### Choose the list Pick it from the lists the field offers, which come from the steps before this one. Choose **Enter it myself** to type a reference such as `{{ 3.results }}` or paste a JSON list instead. ### Say how many to keep A whole number. Zero is allowed and returns an empty list. ### Choose which end to take them from The beginning of the list, or the end. ## Order decides which ones survive Limit has no opinion about which entries are the most important. It takes them from whichever end you chose, and the entries keep the order they were already in. **So sort first if "the top ten" means anything more than "any ten".** A [Sort](/build/action-steps/sort) ahead of Limit turns it into the highest-value ten. Taking from the end is the shortcut when the list is already in the order you want and you need its tail. The ten most recent entries, in a list sorted oldest first. ## A list shorter than the limit comes back whole Asking for ten when the list has four returns all four. It is not an error and nothing is padded. ## What it passes on The shortened list: ``` {{ 5.$full_result }} ``` The step also counts the items for you: `{{ 5.resultCount }}` is how many it passed on, and `{{ 5.results }}` is the list as the data panel offers it. ## Examples to copy ### The three biggest orders - A [Sort](/build/action-steps/sort) step first: field `total`, type Number, descending - Then **Limit**: 3, from the beginning Without the Sort, this is three arbitrary orders. ### A safe test run While building a workflow that emails every customer, a Limit of 2 in front of the email step keeps a mistake from reaching everyone. Remember to remove it before the workflow goes live. ## Limits Limit works on lists of up to 100,000 items. A longer list is refused with a message naming both numbers, rather than being cut short. Split it first. ## What's Next? - [Sort](/build/action-steps/sort) — decide which entries end up first - [Filter](/build/action-steps/filter-items) — keep entries by a test rather than by count - [Loops & Iteration](/build/action-steps/loops) — do something with each entry that survives --- Source: https://docs.getglow.ai/build/action-steps/loops # Loops & Iteration > Two ways to repeat work in Glow: a setting on one step, and the Repeater step for a body of several. Glow repeats work in two ways, and the difference is a shape rather than a rule. **Run for each item** is a setting that runs one step once per entry. The **Repeater** step repeats a body of several steps. Use them to send one message per customer, enrich fifty leads, or page through an API. ## A loop is always something you drew Nothing in Glow starts looping on its own. A step given a list of fifty records does not quietly run fifty times: it runs once, with the list as its input, until you say otherwise. That is deliberate. Repeating is either a setting you switched on, visible on the step's badge, or a Repeater with a line running back into it. Either way you can point at the thing that makes it repeat. Two things follow, and both save time later: - **You can read a canvas and know what it does.** Nobody has to open every step to find the one that fans out over a thousand records. - **You can plan for repeated work.** The loop is visible on the canvas, so you can review the steps and item count before running it. ## Which one? Count the steps **How many steps does the work take per item?** That one question decides it. Two loop shapes: Run for each item fans one step out over a list, while the Repeater sends the flow back around a circuit. See /build/action-steps/loops. | Use | When | | -------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | | **[Run for each item](/build/action-steps/loops/run-for-each-item)** — a setting | One step needs to run once per item. Most cases. | | **[Repeater](/build/action-steps/loops/repeater)** — a step on the canvas | Several steps run together per item, or the body repeats a fixed number of times. | **One step** — sending an email per customer, writing each row to a sheet — needs no loop on the canvas at all. Open that step's repeat badge, switch it to **Each item**, and choose the list. **More than one step** — look the customer up, decide something, write it back — is a loop body, and a body needs the Repeater step. Repeater also covers the cases with no list. **A fixed number of times** runs the body N times, with `{{ item.$index }}` as the counter — that is how you call page 1, 2, 3 of an API. **Until a rule is met** keeps going until what a pass produced satisfies a rule you write, for when the finish line is a result rather than a count. ## Worked examples of each Real jobs, and which mechanism each one needs. | The job | Use | Why | | ---------------------------------------------------------------------- | ------------------------- | ------------------------------------------------------------------ | | Email every person who filled in this week's form | **Run for each item** | One step — the email — per person. | | Write each row of a CSV into a spreadsheet | **Run for each item** | One step per row. | | Post a Slack message for every failed payment | **Run for each item** | One step per payment. | | For each lead: look up the company, score it, write it back to the CRM | **Repeater** | Three steps per lead, and step two needs step one's answer. | | For each invoice: read the PDF, check the total, file it or flag it | **Repeater** | The body branches, so it cannot be one step. | | Fetch pages 1 to 10 of an API | **Repeater**, fixed count | No list to walk — a counter, with `{{ item.$index }}` as the page. | | Retry a flaky call five times with a wait between | **Repeater**, fixed count | The body is a call plus a Wait, repeated a set number of times. | The pattern behind the table: **count the steps the work takes for one item.** One step is a setting; more than one is a Repeater. ## What they share Both bind the current item to `{{ item }}`, so what you write inside the loop is the same either way. | Reference | What it holds | | ------------------- | ------------------------------------- | | `{{ item }}` | The entry being processed | | `{{ item.$index }}` | Position in the list, counting from 0 | | `{{ item.$total }}` | How many items there are altogether | Both also collect what every pass produced into `{{ N.results }}`, with `{{ N.items }}` carrying per-item detail and `{{ N.stats }}` the totals. > **[Filter](/build/action-steps/filter-items) uses a different form.** Its > rules take `{{ $item }}` and `{{ $itemIndex }}`, with the dollar sign at the > front. Filter evaluates its rules itself rather than running a pass per item, > which is why the two look alike but are not interchangeable. ## One mechanism per list, never both A step inside a Repeater's body already runs once per pass, so its own **Each item** mode is switched off there. Going the other way, the canvas refuses a connection that would close a loop around a step set to repeat on its own. **Why it works that way:** both answer the same question, which is how many times a step runs. Each has its own answer. A step set to **Each item** over 50 records, inside a Repeater running 50 passes, would run 2,500 times. Almost nobody means that, and it is expensive to discover by running it. So the rule is one mechanism per list: - Work that takes **one step** per item: the step's own **Each item** setting. - Work that takes **several steps** per item: a Repeater, with those steps plain inside its body. For a list inside a list — every order for every customer — use two Repeaters in sequence rather than one inside the other. Flatten the inner list with a [Combine](/build/action-steps/combine) step first, then loop over the result. ## Plan for loop usage Usage depends on the steps that run and the work they perform on each pass. Standard action steps consume 1 Step credit per item pass, while control steps are free. See [Step Credits, Tokens & Storage](/manage/billing/credits-and-allowances) for how repeated work and failed attempts are counted. Where the list comes from somewhere you do not control, cap it — with Repeater's **Safety limit**, or with a [Filter](/build/action-steps/filter-items) before it reaches the loop. See [System Limits](/reference/system-limits) for the concurrency your plan allows. ## What's Next? - [Run for each item](/build/action-steps/loops/run-for-each-item): the setting, in full. - [Repeater](/build/action-steps/loops/repeater): the step, its circuit, and how to pace it. - [Variable Syntax](/reference/variable-syntax): the full reference for `{{ }}`. --- Source: https://docs.getglow.ai/build/action-steps/loops/repeater # Repeater > The step that repeats a body of several steps: once per item in a list, a fixed number of times, or until a rule is met. **Repeater** repeats a body of steps: look the customer up, decide something, write it back, then round again. It runs once per item in a list, a fixed number of times with no list at all, or until a rule you write is met. > Dock: Flow · Takes: a list, or a count · Returns: a list of results Where one step does the work per item, the [Run for each item](/build/action-steps/loops/run-for-each-item) setting is simpler and needs nothing on the canvas. Repeater is for a body. ## It is a circuit, not a block This is the one thing worth understanding before you build anything. Repeater has two outputs. **Each time** is the body of the loop, and **When finished** is what happens after it. The steps you connect to **Each time** run once per pass. What makes the loop repeat is the connection **back**: the last step of the body has to connect into the Repeater step again. That returning line is what starts the next pass. Two loop shapes: Run for each item fans one step out over a list, while the Repeater sends the flow back around a circuit. See /build/action-steps/loops. Think of it as a circuit rather than a container. The work does not sit inside Repeater; it flows out of **Each time**, through your steps, and back in. > **Check the Execution log after your first test run.** Open it from the > toolbar and count the steps: Repeater and every step in its body should be > there, the body's steps once per pass. > > A run can report that every step succeeded while listing only the steps before > the loop. That is the signal the circuit is not carrying: the returning > connection is the first thing to check. ## Setting it up ### Add the step Open **Tools** in the dock and choose **Repeater** under Flow. ### Choose how to repeat **How to repeat** offers three modes: - **Once per item in a list**: one pass per item. The usual choice. - **A fixed number of times**: a count, with no list involved. Useful for paging through an API or a retry ladder you control yourself. - **Until a rule is met**: passes keep going until what the last one produced satisfies a rule you write. Reach for it when the finish line is a result rather than a count — a paged API that says when it has no more pages, or a job you poll until it reports done. ### Point it at the list For list mode, **List** takes a reference to a list an earlier step produced, for example `{{ 3.result }}`. Insert it from the data icon rather than typing it. ### Group items into batches, if one call can take several **Items per batch** starts at 1, so each pass handles one item. Raise it and each pass receives a group of items instead. Inside the body, `{{ item }}` then holds the whole group as a list, and the data selector labels it **Current batch**. The final group can be smaller when the list does not divide evenly. Group items when one call can handle many records at once, such as an API that accepts fifty rows per request: fewer passes finish a long list sooner. ### Build the body Connect the first step of your loop to the **Each time** output, then chain as many steps as the work needs. ### Close the loop Connect the last step of the body **back into the Repeater step**. This is what makes it repeat. ### Add what comes after Anything connected to **When finished** runs once, after every pass is done. ## Referring to the current item Inside the loop, `{{ item }}` is the whole item and `{{ item.field }}` is one of its properties. Two more give you the position: | Reference | What it holds | | ------------------- | ------------------------------------- | | `{{ item.$index }}` | Position in the list, counting from 0 | | `{{ item.$total }}` | How many items there are altogether | **Both sit under `item`.** Write `{{ item.$index }}`, not a bare `{{ $index }}`. ## Steps in the body do not loop on their own A step inside the body already runs once per pass, so its **Each item** setting is switched off there. You do not need it: `{{ item }}` inside the body is the entry this pass is working on. **Why they cannot be combined:** both decide how many times a step runs, and each has its own answer. A step set to Each item over 50 records, inside a Repeater running 50 passes, would run 2,500 times. Almost nobody means that, and it is an expensive thing to find out by running it. For a list inside a list — every order for every customer — use two Repeaters in sequence rather than one inside the other. Flatten the inner list with a [Combine](/build/action-steps/combine) step first, then loop over the result. ## What decides when the loop stops In the first two modes the number of passes is settled before the first one runs: it comes from the list's length, or from the count you typed. Nothing that happens inside the loop changes it. **Until a rule is met** works the other way round. Repeater runs a pass, checks your rule against what that pass produced, and either stops or goes round again. The count is decided one pass at a time, which is what lets the loop finish on a result rather than on a number. Two things follow from that, and both are worth knowing before you build one: - **The passes run one after another.** A pass cannot start before the one before it has been judged, so **Passes running at once** is held at 1 in this mode whatever it says. - **The rule may only read the pass that just finished**, plus `{{ item.$index }}` and `{{ item.$total }}`. Point it at a step outside the loop and the run stops with a message saying the reference is outside the completed pass. **Safety limit** is what guarantees the loop ends. In this mode reaching it fails the step rather than finishing quietly, so the run tells you the rule was never met instead of leaving you to wonder whether it was. ## Controlling the pace **Passes running at once** sets how many passes run concurrently (up to **10**, depending on your plan tier; see [System Limits](/reference/system-limits)). - **Higher values:** Finish long lists sooner. - **Lower values:** Avoid rate limits on external services. - **Until a rule is met:** Stays at 1 because each pass must be evaluated before the next begins. Most third-party APIs have a rate limit, and a loop is the easiest way to meet it. If you start seeing `429` responses, lower this before changing anything else. **Safety limit** caps how many passes the loop may run (default **10,000**). Lower this when processing external lists to prevent runaway loops. In **Until a rule is met** mode, hitting the safety limit deliberately fails the step so you know the condition was never satisfied. > **Plan for repeated work.** Usage depends on the steps that run in the loop > and the work they perform on each pass. See [Step Credits, Tokens & > Storage](/manage/billing/credits-and-allowances). A list whose length you do > not control is worth capping: with **Safety limit**, or with a > [Filter](/build/action-steps/filter-items) before it reaches the loop. ## When a pass fails **If a pass fails** decides what happens next, and it defaults to **Stop the run**. | Setting | What happens | | -------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | | **Stop the run** (default) | The loop stops. Passes already done keep what they did: a message sent is still sent. The rest of the list is not attempted. | | **Skip it and carry on** | The remaining passes run. Failures are recorded, and the step finishes successfully. | Which you want depends on the work. Sending a hundred notifications: skip, and chase the failures afterwards. Writing a hundred rows that must all land together: stop, and fix the cause before rerunning. > **With skipping on, check `{{ N.items }}` rather than the step's status.** The > step finishes green and `{{ N.results }}` is simply shorter than the list you > put in. If anything downstream depends on every item having been processed, > `items` is where the per-item detail lives. ## What it passes on The loop collects what every pass produced into `{{ N.results }}`, where `N` is the step number. **That collection is itself a list**, so it appears in the next step's data selector like any other list, and a later step can loop over it in turn. | Reference | What it holds | | ----------------- | -------------------------------------------------------- | | `{{ N.results }}` | Successful results only, in the order of your input list | | `{{ N.items }}` | Every item with its position, status and error | | `{{ N.stats }}` | `total`, `succeeded`, `failed`, `skipped` | `stats` is what you want for a summary message. "Processed 47 of 50" needs no counting on your side. **`results` holds successes only, in your input order.** When several passes run at once they finish in whatever order the other system answers, and the results are then reordered to match the list you supplied. So `{{ 5.results }}` is never in completion order. The consequence worth planning for: **when some passes fail, `results` is shorter than the list you put in.** Failures are left out rather than held as empty slots, so position 3 of the results is not necessarily the third item you sent. When you need results lined up against your input, or you need to see what failed, use `{{ N.items }}`: it carries every item with its position, status and error. > **A very large loop fails rather than passing on a partial answer.** When the > collected results exceed the size Glow carries between steps, the step is > marked failed with a message saying so. The work in each pass still happened: > the results were too large to hand on. Process the list in smaller pages, or > return less per item: an id and a status reach the ceiling far later than > whole documents. ## Examples to copy ### Send one message per person **How to repeat:** Once per item in a list. **List:** `{{ 2.ret.records }}`. Connect a Slack **Send Message** step to **Each time**, addressed to `{{ item.email }}` with a body using `{{ item.name }}`. Connect it back into Repeater. Set **Passes running at once** to 3 so Slack is not hit all at once. ### Enrich a list, then summarise it **List:** `{{ 1.leads }}`. On **Each time**, an HTTP Request to your enrichment provider, then back into Repeater. On **When finished**, an AI Prompt with `Summarise these results: {{ 4.stats }}`. It runs once, with the totals for the whole loop. ### Page through an API **How to repeat:** A fixed number of times. Set **Number of passes** to the number of pages you want. On **Each time**, an HTTP Request using `{{ item.$index }}` as the page number, then back into Repeater. **Safety limit** stops it if the count is ever miscalculated. ### Space out requests to a strict API Put a [Wait](/build/action-steps/delay) of 2 seconds as the last step of the loop body, before the connection back into Repeater. Each pass then waits before the next begins, and the requests arrive spread out rather than all at once. ## What's Next? - [Run for each item](/build/action-steps/loops/run-for-each-item) — the simpler setting, when one step does the work. - [System Limits](/reference/system-limits) — concurrency and per-item retries by plan. - [Variable Syntax](/reference/variable-syntax) — the full reference for `{{ }}`. --- Source: https://docs.getglow.ai/build/action-steps/loops/run-for-each-item # Run for Each Item > A setting on a single step that runs it once per entry in a list, with no loop drawn on the canvas. **Run for each item** is a setting on a step, not a step of its own. Bind it to a list and that step runs once per entry, leaving the canvas a straight line. > Dock: A setting on any step · Takes: a list · Returns: a list of results It covers the common case: send one email per customer, write one row per order, create one ticket per report. If the work takes more than one step per item, you need the [Repeater](/build/action-steps/loops/repeater) step instead. Two loop shapes: Run for each item fans one step out over a list, while the Repeater sends the flow back around a circuit. See /build/action-steps/loops. ## Setting it up ### Open the step's repeat badge Click the step on the canvas and find the small badge on it — the one whose tooltip reads _"Run this step once per item in a list"_. It carries no label until you set it, and then shows what it is doing: **Each item**, or **Pick a list** if it still needs one. ### Switch it to Each item The default is **Once**, meaning the step runs a single time. **Each item** is the setting that makes it repeat. ### Choose the list Point it at a list an earlier step produced, such as `{{ 3.result }}`. Insert the reference from the data icon rather than typing it: the path depends on which step made the list. ### Write the step's fields against one item Inside the step, `{{ item }}` is the entry being processed right now. A field that would have read `{{ 3.result.0.email }}` becomes `{{ item.email }}`. ## Referring to the current item | Reference | What it holds | | ------------------- | ------------------------------------- | | `{{ item }}` | The whole entry being processed | | `{{ item.field }}` | One property of it | | `{{ item.$index }}` | Position in the list, counting from 0 | | `{{ item.$total }}` | How many items there are altogether | **Both position tokens sit under `item`.** Write `{{ item.$index }}`, not a bare `{{ $index }}` — that one is not recognised and passes through as literal text. > **[Filter](/build/action-steps/filter-items) uses a different form.** Its > rules take `{{ $item }}` and `{{ $itemIndex }}`, with the dollar sign at the > front. Filter evaluates its rules itself rather than running a pass per item, > which is why the two look alike but are not interchangeable. ## Watching it run A step repeating over a list counts up on the canvas as it goes. Its badge reads `0/12` when the run starts and climbs, so a long fan-out reads as moving rather than stuck. The badge counts **runs of that step**, one per item. Turning on the option that flattens each item's output changes how many items there are, and the count follows it. ## Not every step offers it The badge is absent on steps where running once per item makes no sense: - **Flow control** — [Conditions](/build/action-steps/conditions), [Switch](/build/action-steps/switch), [Filter](/build/action-steps/filter-items). These already work through a list themselves. - **Steps that take their own inputs** — [Combine](/build/action-steps/combine) names the two lists it joins, and a [Repeater](/build/action-steps/loops/repeater) already decides how many times its body runs. - **Steps that wait** — [Wait](/build/action-steps/delay), [Human Review](/build/action-steps/user-approval). To pause between passes, put a Wait inside a [Repeater](/build/action-steps/loops/repeater) body instead. - **Steps with no per-item work** — [Stop and Error](/build/action-steps/stop-and-error), Do Nothing. - **Triggers**, which start the run rather than doing work in it. ## You cannot use it inside a Repeater A step in a [Repeater](/build/action-steps/loops/repeater) body already runs once per pass, so **Each item** is switched off for it. The canvas also refuses a connection that would close a loop around a step already set to repeat on its own. **Why:** both answer the same question — how many times does this step run — and each has its own answer. A step set to Each item over 50 records, inside a Repeater running 50 passes, would run 2,500 times. That is almost never what anybody means, and an expensive thing to discover by running it. Inside a Repeater body, leave the step alone: the Repeater is already driving the repetition, and `{{ item }}` works exactly the same way. ## What it passes on The step collects what every run produced, and the collection is itself a list. A later step reads it the same way it reads any other list, and can loop over it in turn. | Reference | What it holds | | ----------------- | -------------------------------------------------------- | | `{{ N.results }}` | Successful results only, in the order of your input list | | `{{ N.items }}` | Every item with its position, status and error | | `{{ N.stats }}` | `total`, `succeeded`, `failed`, `skipped` | > **When two steps loop over the same list, use `{{ N.item }}` rather than > `{{ N.results }}`.** It gives you what step `N` produced **for the item being > processed right now**, so "the record step 5 created for this same lead" stays > correct. See [Variable > Syntax](/reference/variable-syntax#reading-an-earlier-looping-steps-result-for-this-item). ## Limits **How long the list may be depends on your plan.** A list longer than your tier's ceiling is refused when the step fans out, with a message naming the limit. See [System Limits](/reference/system-limits#batch-processing-run-for-each-item) for the figure on your plan, and for how many items may process at once. Each item repeats the step's work. Standard action steps consume 1 Step credit per item processed, while control steps are free. See [Step Credits, Tokens & Storage](/manage/billing/credits-and-allowances) for how repeated work and failed attempts are counted. Where the list comes from somewhere you do not control, put a [Filter](/build/action-steps/filter-items) or a [Limit](/build/action-steps/limit) in front of it. A list that arrives ten times longer than expected then stops at a number you chose. See [System Limits](/reference/system-limits) for how many runs may happen at once on your plan. ## Examples to copy ### One email per customer An earlier step returns `{{ 2.ret.records }}`. Set the email step's repeat badge to **Each item** and point it at that list. Address it to `{{ item.email }}` and write the body with `{{ item.name }}`. ### Number each row as you write it Writing rows to a sheet, use `{{ item.$index }}` for a sequence number and `{{ item.$total }}` to write "3 of 47" into a status column. ## What's Next? - [Repeater](/build/action-steps/loops/repeater) — when the work takes more than one step per item. - [Loops & Iteration](/build/action-steps/loops) — the choice between the two, in one page. - [Variable Syntax](/reference/variable-syntax) — the full reference for `{{ }}`. --- Source: https://docs.getglow.ai/build/action-steps/no-operation # Do Nothing > A pass-through utility step that organizes your canvas without modifying data. **Do Nothing** is a step that does nothing, on purpose. > Dock: Flow · Takes: any value · Returns: the same value, untouched Use it to join several branches back into one line, or to keep a long connection tidy. It also works as a placeholder for a step you have not built yet. Data passes through unchanged. ## Setting it up Place it on the canvas and connect it. It has no fields to fill in. ### Connect the incoming branches Route several branches into one Do Nothing step and they meet at a single point. ### Connect what comes next Connect its output to the step that should follow. The data arriving from the incoming branches passes through completely unaltered. ### Name it Rename the step to say what it is for: "Merge notifications", "Add the API call here". A canvas of steps all called Do Nothing tells the next person nothing. ## What it passes on Whatever reached it, untouched. Because only one branch of a Switch normally runs, the step carries that branch's data through. In practice you rarely reference the Do Nothing step itself. Reference the step that produced the value instead, so `{{ 3.ret.email }}` rather than this step's number. That path is the same whichever branch the run took, and it survives you deleting the merge point later. ## Examples to copy A Switch step may trigger three different actions (Slack, Email, SMS). Merge all three branches back into a single **Do Nothing** step before the final database update. This keeps the canvas clean. Long connection lines stretching across the canvas are hard to follow. Insert a Do Nothing step as a visual routing pin, and steer the lines around other steps. When drafting a new workflow, use Do Nothing steps as placeholders for actions you intend to build later. You can lay out the full structure before building any of it. ## What's Next? - See the branching steps a Do Nothing step usually merges back together in the [Switch Step](/build/action-steps/switch). - Learn the rest of the canvas layout in [Steps and Connections](/build/core-concepts/steps-and-the-canvas). --- Source: https://docs.getglow.ai/build/action-steps/parse-json # Parse JSON > Break one block of text into separate fields, so later steps can pick out the values inside it. **Parse JSON** breaks one block of text into separate fields, so each value becomes something later steps can pick from. Use it when data arrives as one long block and you need one value inside it, like the customer's name. > Dock: Data · Convert · Takes: a block of text · Returns: separate fields **Keyboard shortcut:** `t+p` ## How to tell you need it Open the earlier step's result. Look for a value sitting inside one long line of text wrapped in braces and quote marks. If the data picker offers you that whole line but not the name inside it, this is the step that opens it up. It usually comes from one of four places. A webhook that sent its data as text, an app that answered with text rather than named fields, a database column holding JSON, or a `.json` file you have just read. ## Setting it up 1. Press `t+p`, or open **Tools → Data → Convert** in the dock and select **Parse JSON**. 2. In the **Input** field, select the data reference that contains your JSON string (for example, the response body from a previous HTTP Request step). 3. Save the step. Glow detects the structure of the parsed JSON automatically. Each field then appears as a selectable output in the data reference picker for downstream steps. ## What it passes on Every value inside becomes its own field on the step, read with the step's own number in front — `4` in the examples below. Nothing is wrapped. - **A record inside a record** is reached with another dot: `{{ 4.customer.name }}`. - **A list** uses the item's position as the next dot, counting from zero, so `{{ 4.items.0.sku }}` is the first. Square brackets such as `items[0]` are not recognised. - **Deeper than that** follows the same pattern all the way down: `{{ 4.results.2.metadata.tags.0 }}`. ## Examples to copy ### Reading an app's answer Suppose an HTTP Request step calls `https://api.example.com/orders/12345` and returns the following response body as a string: ```json { "order_id": 12345, "customer": { "name": "Acme Corp", "email": "orders@acme.com" }, "items": [ { "sku": "WIDGET-A", "quantity": 10 }, { "sku": "WIDGET-B", "quantity": 5 } ], "total": 249.99 } ``` Say the Parse JSON step is step 4. Each value inside is then its own field, read with this step's number in front: | Reference | Value | | -------------------------- | ------------------- | | `{{ 4.order_id }}` | `12345` | | `{{ 4.customer.name }}` | `"Acme Corp"` | | `{{ 4.customer.email }}` | `"orders@acme.com"` | | `{{ 4.items.0.sku }}` | `"WIDGET-A"` | | `{{ 4.items.0.quantity }}` | `10` | | `{{ 4.items.1.sku }}` | `"WIDGET-B"` | | `{{ 4.total }}` | `249.99` | You can now use `{{ 4.customer.name }}` in a Slack message step, or `{{ 4.total }}` in a Conditions step. The data picker shows these as `customer.name` and `total`, without the step number. That is the label, not the reference. Pick the value from the picker and Glow inserts the full form for you. ## Limits ### When the text is malformed If the text is not properly formed JSON, the step fails and the error names what it choked on. Open the earlier step's **Executions** tab and read the raw value — that is almost always quicker than guessing which character is wrong. Where the text comes from a system you do not control and is reliably malformed, put a [Code editor](/build/action-steps/code-execution) step in front to tidy it first. ### When nothing arrives An empty input produces no output, so a later step reading one of these fields finds nothing there and fails. Where the source sometimes sends nothing, put a [Conditions](/build/action-steps/conditions) step in front testing the input with **is not empty**. ### When it is very large A very large piece of JSON takes the step longer to read. If you only need a few fields out of it, pull those out with a [Code editor](/build/action-steps/code-execution) step first. > **Escaped characters need no work from you.** A string full of `\"` and `\n` > comes out with those turned back into quote marks and line breaks. There is > nothing to clean up first. ## What's Next? - Reference the newly exposed fields downstream using the [Variable Reference Syntax](/reference/variable-syntax). - Review the shapes Glow recognizes in [Data Types & Structures](/build/core-concepts/data-types). --- Source: https://docs.getglow.ai/build/action-steps/remove-duplicates # Remove Duplicates > Keep each entry in a list only once. The **Remove duplicates** step returns a list with repeats taken out. You say what makes two entries the same, and it keeps one of each. > Dock: Data · Lists · Takes: a list · Returns: the list with repeats removed Use it after combining sources that overlap. The same customer exported from two systems, an address on several orders, a lead that came in twice. > **Deduplicating inside a single field?** You can remove duplicates directly inside field tokens using [Data Transformation](/build/core-concepts/data-transformation) with `{{ 2.emails | remove_duplicates }}` (or `{{ 2.records | pluck:email | remove_duplicates }}`). Use this dedicated **Remove duplicates** step when deduplicating complex multi-field records or processing large datasets (up to 100,000 items). ## Setting it up ### Choose the list Pick it from the lists the field offers, which come from the steps before this one. Choose **Enter it myself** to type a reference such as `{{ 3.results }}` or paste a JSON list instead. ### Say what makes two entries the same For a list of plain values, the value itself. For a list of records, name the field, such as `email` or `customer_id`. ### Choose which copy to keep The first one seen, or the last. ## Which copy you keep changes the data, not the order **The surviving items stay in the order they first appeared**, whichever copy wins. Keeping the last one does not reverse anything. What changes is _which_ record survives, and with real data that matters. Say a customer appears twice, once with an old address and once with a new one. **First** keeps the old, **Last** keeps the new, assuming the newer record came later in the list. For a list of plain values it makes no difference at all: the same value is the same value. ## Capital letters count unless you say otherwise **Ignore capitalisation** is off to begin with, so `Active` and `active` are two different values and both survive. Turn it on when the same thing is spelled differently by two systems: one exports upper case, the other lower. ## What it passes on The list with repeats removed: ``` {{ 5.$full_result }} ``` Shorter than the input, or the same length when nothing repeated. The step also counts the items for you: `{{ 5.resultCount }}` is how many it passed on, and `{{ 5.results }}` is the list as the data panel offers it. ## Examples to copy ### One email per person Two exports combined into one list, some people in both. - **Field**: `email` - **Keep**: Last - **Ignore capitalisation**: on _Last_ is the choice when the second export is the more recent one and its values should win. Ignoring capitalisation catches `Anna@example.com` and `anna@example.com` as one person. ### Unique tags A list of plain text values with repeats. - **Field**: leave empty, so the value itself is compared - **Keep**: First ## Limits Remove duplicates works on lists of up to 100,000 items. A longer list is refused with a message naming both numbers. ## What's Next? - [Combine](/build/action-steps/combine) — merge two lists before removing repeats - [Sort](/build/action-steps/sort) — decide which copy counts as first - [Summarize](/build/action-steps/summarize) — count what is left --- Source: https://docs.getglow.ai/build/action-steps/routing # Choosing a Flow Step > Which of the Flow steps decides where a run goes, which divides a list, and which pauses or ends it. The **Flow** group holds the steps that decide what happens next rather than doing outside work. Three of them route, and picking between those three is the choice people get wrong most often. ## The three that route They look alike in the panel because they share a rule builder. They answer three different questions. | | The question it answers | Works on | How many routes run | | ------------------------------------------------ | ------------------------- | -------------------- | ----------------------------- | | **[Conditions](/build/action-steps/conditions)** | Which of these are true? | the whole run | every one that matches | | **[Switch](/build/action-steps/switch)** | Which one of these is it? | the whole run | the first that matches, only | | **[Filter](/build/action-steps/filter-items)** | Which items qualify? | a list or one record | both, each with its own items | ### Start with what you are holding **A list?** That is Filter. It checks each item on its own and sends the matching ones down KEPT, the rest down DISCARDED. Both routes carry on with their share, so you can email the ones that passed and log the ones that did not. **One thing, and a path to choose?** That is Conditions or Switch. Neither divides anything — they look at the run as a whole and decide which way it goes. ### Then ask how many answers you want **Can more than one be true at once?** Use Conditions. "Is it over £10,000?" and "Is it in the UK?" can both be yes, and both routes run. That is the thing a true/false switch cannot do, and the reason Conditions exists. **Is it one of several?** Use Switch. A ticket is a refund, a complaint or a question — not two of them. Cases are checked top to bottom and the first match wins, so ordering matters and a general case belongs at the bottom. > **A single record through a Filter is a useful gate.** "Only carry on if this > order is over £500" works as a one-item Filter, and the difference from a > Conditions step is what travels: the Filter sends the record itself down the > route, where Conditions sends only the decision. ## What they share All three are built from the same rule builder, so once you have written a rule in one you can write it in any of them: - **Pick the data, pick a type, pick an operator, give it a value.** Six types cover most tests: String, Number, Boolean, Object, Array, Date & Time. - **Rules in a group are joined with AND; groups are joined with OR.** You choose which you are adding, not the combinator itself. - **Text is compared exactly** unless you say otherwise. `Active` does not match `active`. - **A rule can be written in plain language** instead, as an **AI Condition** — in Conditions and Filter. **Switch is typed-only**, deliberately: it picks one route out of several and stops, so the answer decides where the whole run goes. That is the place you want a comparison rather than a judgement. The full operator catalogue lives on the [Conditions](/build/action-steps/conditions#operators) page and applies to all three. ## The rest of the Flow group The other Flow steps do not route on a rule. They change what happens next in some other way. | Step | What it does | | -------------------------------------------------------- | ----------------------------------------------------------------------- | | **[Loops & Iteration](/build/action-steps/loops)** | Repeats work — one step per item, or a body of several | | **[Subflows](/build/action-steps/trigger-workflow)** | Runs another workflow and waits for its answer | | **[Wait](/build/action-steps/delay)** | Pauses the run for a set time | | **[Human Review](/build/action-steps/user-approval)** | Pauses until a person approves or rejects | | **[Stop and Error](/build/action-steps/stop-and-error)** | Ends the run deliberately, with a message you write | | **[Do Nothing](/build/action-steps/no-operation)** | Ends a branch tidily, so a route that should do nothing still completes | ## What's Next? - Route on several things at once with [Conditions](/build/action-steps/conditions). - Pick one of several with [Switch](/build/action-steps/switch). - Split a list with [Filter](/build/action-steps/filter-items). --- Source: https://docs.getglow.ai/build/action-steps/sort # Sort > Put a list in order by one of its fields, smallest first or largest first. The **Sort** step puts a list in order. You name the field to sort by and the direction, and it returns the same items rearranged. > Dock: Data · Lists · Takes: a list · Returns: the same list, reordered Use it when order carries meaning: the highest-value orders first, the oldest tickets first, names in alphabetical order for a report someone will read. > **Sorting inside a field?** You can sort or reverse lists directly inside field tokens using [Data Transformation](/build/core-concepts/data-transformation): > - Sort alphabetically or by field: `{{ 2.records | sort:ascending:name }}` > - Reverse ordering: `{{ 2.records | reverse }}` > > Use the **Sort** canvas step when you need multi-level tie-breaking rules (e.g. priority then date) or when dealing with large datasets (up to 100,000 items). ## Setting it up ### Choose the list Pick it from the lists the field offers, which come from the steps before this one. Choose **Enter it myself** to type a reference such as `{{ 3.results }}` or paste a JSON list instead. ### Add a **Sort by** row Name the field inside each item, such as `total` or `created_at`. Leave the field blank to compare each whole item, which suits a list of plain values. ### Choose the direction Ascending puts the smallest first, descending the largest. **Type** starts as **Automatic**: Sort reads the values and works out whether they are text, numbers or dates. Pick **Text**, **Number** or **Date** yourself only when you want to override that. ### Add more rows if one is not enough Up to ten, applied in the order they are listed. The second row only decides ties in the first. ## Breaking ties with a second rule One rule sorts by one thing, and leaves entries that share that value in whatever order they arrived. **Add a second row to decide those.** Sort support tickets by priority, then by date. The urgent ones come first, oldest of them at the top: | Row | Field | Type | Direction | | --- | ------------ | ---- | --------- | | 1 | `priority` | Text | Ascending | | 2 | `created_at` | Date | Ascending | Rows are applied in order, so the first one is the one that matters most. ## Automatic works the type out for you Text, numbers and dates sort differently, and the same list comes back in a different order depending on which one Sort uses. Sorted as text, `100` comes before `20`, because text is compared character by character and `1` comes before `2`. Sorted as numbers, `20` comes first. **Type** starts as **Automatic**: Sort reads every value in the column and picks the one type they all share. When the list has run data, the panel shows what it decided, such as _Automatic - Number_. If the values disagree, say dates mixed with plain text, the run stops and names the two items that conflict. Choose **Text**, **Number** or **Date** yourself and the run continues with your answer. An explicit type is also how you override the guess, for example numeric product codes that should sort as text. **If the type you choose does not match what is in the list, Sort refuses rather than guessing.** It names the item it could not read, so you can open that entry and see what it holds. ## Capital letters affect text ordering Sorting text is exact by default, and capitals sort before lower case, so `Zebra` comes before `apple`. **Ignore capitalisation** turns that off, and the list orders the way a person would expect. Leave it off when case carries meaning, as it does in product codes. ## What it passes on The same items in the new order: ``` {{ 5.$full_result }} ``` Nothing is added or removed. A list of ten comes back as a list of ten. The step also counts the items for you: `{{ 5.resultCount }}` is how many it passed on, and `{{ 5.results }}` is the list as the data panel offers it. ## Examples to copy ### Highest value first Orders from a shop, largest first, so a report can lead with them. - **Field**: `total` - **Type**: Number - **Direction**: Descending Follow it with a [Limit](/build/action-steps/limit) to keep only the top few. ### Oldest ticket first A support queue, so the longest-waiting request is dealt with first. - **Field**: `created_at` - **Type**: Date - **Direction**: Ascending Automatic picks both of these types up on its own. Set the type explicitly when you want to pin it. ## Limits Sort works on lists of up to 100,000 items and takes at most ten **Sort by** rows. Beyond either, it refuses and says so rather than returning a partial answer. Split the list first, or drop the least important rule. ## What's Next? - 👉 **[Limit →](/build/action-steps/limit)**: take the first few items after sorting them. - **[Filter](/build/action-steps/filter-items)**: remove entries before sorting the list. - **[Summarize](/build/action-steps/summarize)**: count or total a list instead of reordering it. --- Source: https://docs.getglow.ai/build/action-steps/split-text # Split Text > Cut one piece of text wherever a character appears, and take the piece you want or all of them. Split Text cuts one piece of text wherever a character you choose appears, then hands back either one piece or the whole list. > Dock: Data · Convert · Takes: text · Returns: one piece or a list of pieces Use it when something arrives as one line, like `"enterprise, b2b, high-priority"`, and you need the parts separately. Or when you want one piece out of the middle: the domain from an email address, the surname from a full name. > **Quick extractions without an extra step:** If you only need a single slice or quick domain/list split, you can do it directly inside field tokens with [Data Transformation](/build/core-concepts/data-transformation): > - Domain from email: `{{ 2.email | extract_domain }}` > - Surname: `{{ 2.name | after:" " }}` > - Split to list: `{{ 2.tags | split:comma_space }}` > > Use the **Split Text** step when you need to pass an entire list of pieces to downstream canvas steps (like a [Loop](/build/action-steps/loops)). **Keyboard shortcut:** `t+s` ## Setting it up Three fields, all required. ### Input The text you want to cut. Pick it from an earlier step with the data picker. ### Separator The character or word to cut on. A comma, a single space, `\n` for line breaks. It also accepts a **regular expression**, which is a pattern language for matching text. That is worth reaching for when the separator varies: a comma that sometimes has a space after it, say. If you do not write patterns, a plain character covers most cases. ### Segment Index **Which piece you want back.** This is the field that decides whether you get one piece or all of them: | Choose | You get | | ------------------ | ------------------------------------- | | **First** | Everything before the first separator | | **Second** | The second piece | | **Last** | Everything after the final separator | | **Second to Last** | The piece before the last one | | **All** | Every piece, as a list | You can also type a number instead: `3` takes the third piece, `-2` counts back from the end. > **Pick All when you want to loop over the pieces.** The named options each > return a single piece of text, which is what you want for "the domain" or "the > surname". For "do something with each tag", choose **All** and point a looping > step at the result. ## What it passes on The piece you asked for (or, with **All**, the list of pieces), nested under `ret`: ``` {{ 4.ret }} ``` Run it once and read the step's **Executions** tab for the exact shape before you build on it; that is quicker than reasoning it out. ## Examples to copy ### Every tag from one field A CRM sends a contact's tags as `"enterprise, b2b, high-priority"` and you want to act on each. | Field | Value | | ----------------- | ------------------- | | **Input** | the tags field | | **Separator** | `, ` (comma, space) | | **Segment Index** | All | Cutting on comma-and-space rather than the comma alone keeps a leading space off every piece after the first. `" b2b"` and `"b2b"` are different text, and a later [Filter](/build/action-steps/filter-items) matching on `b2b` would miss the first form. Then turn on **Run for each item** on the next step and point it at this one. See [Loops & Iteration](/build/action-steps/loops). ### A surname from a full name A form gives you `"Jane Doe"` and the greeting needs the surname. | Field | Value | | ----------------- | ----------- | | **Input** | `full_name` | | **Separator** | a space | | **Segment Index** | Last | **Last** rather than **Second**, so a double-barrelled surname or a middle name does not break it. ### The domain from an email address | Field | Value | | ----------------- | ----------- | | **Input** | the address | | **Separator** | `@` | | **Segment Index** | Last | Useful in front of a [Conditions](/build/action-steps/conditions) step that routes by company. ## What's Next? - Do something with each piece using [Loops & Iteration](/build/action-steps/loops). - Cut the list down before you act on it with [Filter](/build/action-steps/filter-items). --- Source: https://docs.getglow.ai/build/action-steps/stop-and-error # Stop and Error > End a run on purpose with a message explaining why, so it shows up as a failure rather than finishing quietly. **Stop and Error** ends a run on purpose and marks it failed. > Dock: Flow · Returns: nothing Use it where a workflow has reached a situation it should not carry on from. Data that did not check out, a record that came back empty, a permission that is missing. The run then shows up as a problem rather than quietly finishing.  *Write the message the run stops with. Leave it blank and the step uses the standard one.* ```mermaid flowchart LR A[Receive Webhook] --> B{Data Valid?} B -->|Yes| C[Process Data] B -->|No| D[Stop and Error] ``` ## Setting it up When a run reaches this step, it stops immediately. ### Place the Step Add the **Stop and Error** step at the end of a branch where execution should not continue: the ELSE route of a validating Condition, or a Switch's fallback. ### Configure the Error Message Enter a descriptive error message. It appears in the workflow's Execution logs, so your team can see why the run was aborted. ### Attach the data that caused it Optional. Add the values that made the run stop, such as the record ID, the amount, or the status code. They are stored on the failure. When you open the run a week later, the message says what went wrong and this says which record it happened to. ## An empty branch looks like success Leave a branch empty and the run registers as **Completed** when it reaches the end of it. Add a Stop and Error step, and it registers as **Failed** instead. That matters because: - Somebody gets notified. Failed runs raise workspace error notifications; completed ones do not. - The run history tells the truth, so a week of quiet failures does not read as a week of clean runs. - Your message is on the failure, so whoever opens it knows what went wrong without reading the canvas. ## What it passes on Stop and Error returns no output. It records the message and any attached data on the failed run so your team can diagnose it from the execution log. ## Limits The step ends the current branch immediately. Any downstream steps connected after it do not run, so place notifications or cleanup work before Stop and Error or on an error-handling path. ## Examples to copy ### Reject a record that fails validation Place Stop and Error on the ELSE route of a Conditions step. Use a message that names the rule and include the record ID as attached data: ```text Customer record failed validation: email address is missing ``` ### Make a Switch fallback visible Place Stop and Error on the fallback route when every expected value has its own case. Include the value that did not match so the execution log shows what needs a new route. ## What's Next? - Decide which failures should retry before they halt in [Error Handling & Retries](/build/core-concepts/error-handling). - Catch unhandled cases on a fallback branch with the [Switch Step](/build/action-steps/switch). --- Source: https://docs.getglow.ai/build/action-steps/summarize # Summarize > Group a list and work out a count, total, average, smallest or largest for each group. **Summarize** turns a list into grouped results. Pick what to group by, then say what to work out for each group: a count, a total, an average, the smallest or the largest. > Dock: Data · Change · Takes: a list · Returns: a list of groups It answers the questions that otherwise need code. Revenue per account manager, tickets per priority, orders per region. > **Simple total or average without grouping?** You can calculate a single total, average, count, or min/max directly inside field tokens using [Data Transformation](/build/core-concepts/data-transformation): > - Sum of record property: `{{ 3.deals | pluck:amount | total }}` > - Average deal size: `{{ 3.deals | pluck:amount | average }}` > - Largest sale: `{{ 3.deals | pluck:amount | largest }}` > - Count of items: `{{ 3.deals | count }}` > > Use the **Summarize** canvas step when you need to group data by dimensions (e.g. revenue grouped by account manager or ticket counts per status). ## Setting it up ### Add the step Open **Tools** in the dock and choose **Summarize**. ### Point it at a list **List to summarize** takes a reference to a list an earlier step produced, for example `{{ 3.result }}`. Insert it from the data icon rather than typing it. ### Choose how to group **Group by** takes the field whose values become the groups: `region`, `owner`, `priority`. Add more than one row to group by a combination. Leave it empty to treat the whole list as one group. That is how you total a list without splitting it. ### Say what to work out **Work out** takes one row per number you want. Each row picks an operation and what to work on: a field of each record, or each whole item. Count needs neither. | Operation | What it gives you | | ------------------- | ----------------------------------------------------------------- | | **Count** | How many records are in the group | | **Count unique** | How many different values a field has | | **Sum** | The total of a numeric field | | **Average** | The mean of a numeric field | | **Smallest** | The lowest value | | **Largest** | The highest value | | **List the values** | Every value of that field in the group, as a list | | **Join the values** | The same values as one piece of text, with a separator you choose | The last two answer a different question from the rest. Instead of a number about the group, they hand you back what is in it: every email address in a region, say, or those addresses joined by a comma, ready for a To field. ## Summarizing a list of plain values When the list holds plain values rather than records, a list of amounts, say, set **Work on** to **Each whole item**. The panel pre-selects it when the sample list holds plain values. Sum, Average, Smallest and Largest use numbers only; a blank or non-number item follows **If a field is missing**. The output column is named after the operation alone, so a whole-item Sum arrives as `{{ N.groups.0.sum }}`. **Group by** always takes a field. To total a list of plain values, leave Group by empty and work on each whole item. ## What it passes on **The result is always a list under `groups`**, even when everything landed in one group. That way a reference written against one grouping still works when you change the grouping later. | Reference | What it holds | | -------------------- | ------------------------- | | `{{ N.groups }}` | One entry per group | | `{{ N.groupCount }}` | How many groups there are | Each entry carries the fields you grouped by, plus one column per row of **Work out**. The column is named after the operation and the field, so a Sum of `amount` arrives as `sum_amount`: ``` {{ 5.groups.0.region }} first group's region {{ 5.groups.0.sum_amount }} that group's total {{ 5.groups.0.count }} how many records it holds ``` Because `groups` is a list, a [Repeater](/build/action-steps/loops) can run over it: one Slack message per region, for example. **Groups come out in the order they first appear** in the input list, not sorted. ## How values are counted This is the part worth reading before you trust a total. **A missing value is missing, not zero.** Ten rows where one has no amount would average 10% low if the blank counted as zero, and nothing in the result would say so. The same applies to a value that is not a number after conversion: `"n/a"`, `true`, or an empty object. **If a field is missing** decides what happens: - **Report** flags it, so you find out. - **Skip** leaves those records out of that calculation. Both are defensible; the point is that you choose rather than the step choosing for you. **Numbers written as text still add up.** A list arriving through `{{ 3.rows }}` often has amounts like `"1200"` rather than `1200`, because references are resolved as text. Summarize converts them, so a total is not silently zero. **Money does not drift.** Totals accumulate exactly, so `0.1 + 0.2` is `0.3`. **Decimal places** rounds the results. Set it to 2 for currency. ## Limits - **Two rows cannot write the same column.** Two Sums of `amount` would both be `sum_amount`, so the step reports the clash instead of one silently overwriting the other. - **A record missing its group-by field** never lands in a group called "undefined". It follows the same **If a field is missing** setting. ## Examples to copy ### Revenue per account manager **List:** `{{ 3.deals }}` · **Group by:** `owner` · **Work out:** Sum of `amount`, and Count. Gives one entry per owner with `sum_amount` and `count`. Follow it with a Repeater over `{{ N.groups }}` to send each manager their own figure. ### Tickets per priority, as one message **List:** `{{ 2.tickets }}` · **Group by:** `priority` · **Work out:** Count. Then reference the groups directly in a Slack message rather than looping. ### A single total, no grouping **List:** `{{ 4.invoices }}` · **Group by:** empty · **Work out:** Sum of `total`. One group, reached as `{{ N.groups.0.sum_total }}`. ## What's Next? - Run over each group with [Repeater](/build/action-steps/loops). - [AI Data Transform](/build/ai-features/ai-transform) — for pulling values out of text that has no fixed shape. - Reshape records before summarising them with [Change fields](/build/action-steps/change-fields). --- Source: https://docs.getglow.ai/build/action-steps/switch # Switch Step > Route a workflow to one of many named outputs, evaluated top to bottom, with a fallback for everything that matches nothing. The **Switch** step routes a run to one of several outputs. You define ordered **routing cases**: the first case whose conditions are met sends the run down its route, and evaluation stops there. > Dock: Flow · Returns: the case that matched **Reach for Switch when the answer is one of several.** A ticket is a refund, a complaint or a question — not two of them. Where two things can be true at once, [Conditions](/build/action-steps/conditions) is the step, and both routes run. See [Choosing a Flow Step](/build/action-steps/routing) for the difference between the three routing steps. Switch takes the first matching route only, and the run continues down that one. > **Always assign the Fallback route.** It is where a run goes when no case > matches. Without it, unmatched runs stop at the Switch and nothing downstream > fires. > > The fallback sits behind the regular cases, so it needs at least one to sit > behind: **a Switch configured with the fallback alone routes nothing.** For a > single unconditional route, use a case with no conditions instead, since an > empty case matches everything. ## Setting it up The panel is the same rule builder the Conditions step uses, so a case is built the same way: pick the data, pick its type, pick an operator, give it a value. ### Add a routing case Click **Add routing case**. New cases are added at the **bottom**, and since evaluation runs top to bottom, a new case is checked last. Reorder them if the priority is wrong. ### Build its rule Pick the data to test, then choose its type: **Text**, **Number**, **Yes/no**, **Record**, **List**, or **Date & Time**. Then pick an operator from that type's list. The full catalogue is on the [Conditions](/build/action-steps/conditions#operators) page, and it is shared between the two steps. Click **Rule** to add a second rule with **AND** or **OR**. ### Point the case at a step Under **Then go to**, choose where the run continues when this case wins. Wire the route as you build each case rather than at the end: the connection is what anchors a case on the canvas, so a case that leads somewhere is one that stays. If the step it should lead to does not exist yet, **Then go to** creates it for you. ### Assign the fallback Under **Fallback**, choose where everything that matched no case should go. > **A case with a blank rule matches everything.** That makes an empty case a > useful catch-all when you put it last, where first-match-wins turns it into a > default. > > Everywhere else, give each case data, an operator and a value before the first > run. **All three, including the data on the left**, which you pick with the data > button beside the field rather than typing. A rule with a value on the right and > nothing on the left compares an empty value against it, so the case never > matches and the run passes it by. > > Drawing a connection out of a Switch creates its case straight away, so check > them if you wire the canvas before configuring it. ## Naming your outputs Each case takes an optional output name, up to 50 characters. It is the **Output 0** field at the top of the case. The name is echoed on the case's result, so run history reads _"Enterprise leads"_ rather than _"case 2"_. On a Switch with more than two or three cases this is the difference between a readable execution record and a puzzle. ## Capital letters matter here > **`Active` does not match `active`.** The Switch compares text exactly as > written, capitals included. > > **How matching works** is where you change that: it holds the choice of whether > text ignores capitalisation, and whether one matching case runs or every one > does. The [Filter](/build/action-steps/filter-items) step starts from the same > rule, exact by default, so a comparison behaves the same way in both. To match regardless of capitals, turn on **Ignore capitalization** under [How matching works](#how-matching-works). ## Sending to every matching branch By default the first matching case wins and the rest are skipped. You can have the run go down **every** branch that matches instead. That is what you want when your cases are labels rather than a choice, such as tagging an order as both _high value_ and _international_. It is not what you want when the branches are alternatives, because the work then happens more than once. Turn on **Run every matching branch** under [How matching works](#how-matching-works). ## How matching works **How matching works** holds two settings that apply to every case in the step. | Setting | Default | What it does | | ----------------------------- | ------- | ---------------------------------------------------------------- | | **Ignore capitalization** | Off | Turn it on and `Active` matches `active`. | | **Run every matching branch** | Off | Turn it on and every case that matches runs, not only the first. | The fallback route is skipped whenever a case matched, whichever of these is on. Workflows imported from another platform can carry extra options across. Those stay available in a collapsed section beneath these two, so an imported Switch keeps behaving the way it did before the move. ## Choosing a routing Mode The **Mode** setting has two values. The default one is everything described above: the cases' rules decide where the run goes. The other ignores the rules entirely and picks a branch by number. You supply the number, counting from zero. It is there so workflows moved over from other automation tools keep working. **For anything you build in Glow, leave Mode alone.** Rules keep the routing readable on the canvas. A number computed somewhere else does not. ## What it passes on The Switch does not pass data along a branch: the steps after it read the same earlier steps they always would. What it publishes is the **decision**, which is what you want when a run went somewhere you did not expect. Open the **Execution log** from the toolbar and expand the Switch's entry: ``` {"mode":"rules","cases":[{"blocks":[{"conditions":[ {"data":"","value":"NEVER_MATCHES","result":false,"operator":"equals", "conditionType":"string"}],"blockSuccess":false}], "matched":false,"caseIndex":0}]} ``` Read it from the inside out. Each condition shows the two values it compared and its `result`. A block passes when every condition in it passed. A case matches when one of its blocks did, and `caseIndex` says which case it was, counting from zero. That is usually enough to settle a routing question in one look: a rule that looks right but reads `result: false` is comparing something other than what you meant, and the two values beside it say what. ## Examples to copy ### Route support tickets by topic | Case | Output name | Data | Type | Operator | Value | | ------------ | ----------- | ------------------- | ---- | ------------- | -------------- | | 1 | `Billing` | `{{ 2.ret.topic }}` | Text | `is equal to` | `billing` | | 2 | `Bugs` | `{{ 2.ret.topic }}` | Text | `is equal to` | `bug` | | **Fallback** | — | — | — | — | general triage | Name the outputs and the run history reads "Billing" rather than "case 1". ### One webhook, several event types | Case | Output name | Data | Type | Operator | Value | | ------------ | ----------- | ---------------- | ---- | ------------- | ------------ | | 1 | `PR opened` | `{{ 1.action }}` | Text | `is equal to` | `opened` | | 2 | `PR merged` | `{{ 1.action }}` | Text | `is equal to` | `closed` | | **Fallback** | — | — | — | — | a Do Nothing | A fallback pointing at [Do Nothing](/build/action-steps/no-operation) is the deliberate way to say "ignore anything else". It also looks different from having forgotten the fallback entirely. ### Score bands, where order is the whole design | Case | Output name | Data | Type | Operator | Value | | ------------ | ----------- | ------------------- | ------ | ----------------- | -------------------- | | 1 | `Hot` | `{{ 3.ret.score }}` | Number | `is greater than` | `80` | | 2 | `Warm` | `{{ 3.ret.score }}` | Number | `is greater than` | `50` | | **Fallback** | — | — | — | — | the nurture sequence | A score of 90 matches both cases. First match wins, so it goes to `Hot`. That is why the bands must run highest first. Reverse them and every hot lead lands in `Warm`. ## What's Next? - Fire several routes at once, or use plain-language rules, with the [Conditions Step](/build/action-steps/conditions). - Fail loudly on the fallback with the [Stop and Error step](/build/action-steps/stop-and-error). - Merge branches back into one path using the [Do Nothing](/build/action-steps/no-operation). --- Source: https://docs.getglow.ai/build/action-steps/trigger-workflow # Subflows > Run one workflow from inside another with the Subflow step: the caller waits, and the result comes back as ordinary step output. The **Subflow** step runs another workflow from inside this one and waits for its answer. The calling run pauses, the called workflow runs start to finish, and its result comes back as this step's output like any other step's. Reach for it when the same job appears in several workflows. Build "notify the on-call channel" or "create the ticket" once, then call it from everywhere it is needed. **Any workflow in your workspace can be called.** There is nothing to publish or switch on first: you pick the workflow from a list, and it becomes a subflow because you called it. ```mermaid flowchart LR A[Calling Workflow] --> B[Subflow Step] B -->|Runs & Waits| C[Child Workflow] C -->|Returns Data| B B --> D[Next Step] ``` ## Why split a workflow - **Reuse.** One copy of a routine, called from every workflow that needs it. Fix it once and every caller gets the fix. - **Readability.** Five workflows of ten steps are easier to follow than one of fifty. - **Isolation.** Sensitive work stays in its own workflow with its own connections. ## Setting it up ### Add the Subflow step Open **Subflows** in the dock. It lists every workflow in your workspace, ready to drop onto the canvas. ### Pick the workflow to call Click the one you want. No preparation is needed on the workflow you are calling: every workflow in the workspace can be called as a subflow. A workflow that cannot be called is greyed out with the reason: it has no steps yet, or calling it would make a loop. ### Check what it does The App drawer shows a small map of the workflow you picked, so you can see what it does without leaving the canvas. ### Pass the data it needs Give the subflow the values it should work with, referencing the calling workflow's steps by number: ``` {{ 1.email }} {{ 3.customer_id }} ``` Build these from the data picker rather than typing them. What sits after the step number is a path into whatever that step produced, so it changes with the step you read from. ### Use the result The called workflow's output is available on this step like any other, referenced by this step's own number. ## The caller waits, and that is the point This is the difference from calling a workflow over its webhook. A webhook call is posted and forgotten: the caller carries on immediately and never sees the outcome. A Subflow step **suspends the calling run** until the called workflow finishes, then resumes it with the answer. A step after it can use the result, branch on it, or pass it further along. ## What it passes on Two things: ``` {{ 5.output }} what the child workflow declared as its output {{ 5.steps.3.ret.id }} any step inside the child run, by its number there ``` `output` is the child's declared result: the value its own output step returned. `steps` exposes the whole child run, so a field the child never declared can still be reached by naming the child step that produced it. ## Limits - **Three levels deep.** A workflow can call a workflow that calls a workflow. Beyond that the step reports a configuration error rather than running. - **A workflow cannot call itself**, and it cannot call anything that would loop back to it. Glow checks the whole chain before running and refuses a cycle, so a run cannot recurse forever. - **The data passed in is capped at 64 KB.** Pass an identifier and let the subflow fetch what it needs, rather than passing a large record. - **Same workspace only.** The list offers workflows on your own team. > **A run's subflow steps appear in the parent's Execution log**, indented under > the step that called them. One log tells the whole story, so you do not have > to open the called workflow separately to see what it did. ## Calling over a webhook instead Sending an [HTTP Request](/build/action-steps/http-request) to another workflow's [Webhook](/build/triggers/webhook) trigger still works, and it suits a genuinely independent job: something that should start and run on its own while the caller finishes. Two differences decide which to use: | | Subflow step | HTTP Request to a webhook | | ------------------------------- | ------------------- | ------------------------- | | Does the caller wait? | Yes | No | | Does the caller get the result? | Yes | No | | Where does it show in the log? | In the caller's log | In its own run | If a later step needs the answer, use Subflow. ## What's Next? - Read what a run records in [Executions](/build/core-concepts/executions). - Send data to an outside service with [HTTP Request](/build/action-steps/http-request). --- Source: https://docs.getglow.ai/build/action-steps/user-approval # Human Review > Pause a workflow until a person makes a decision. Define the outcomes, the form they fill in, and the branch each choice takes. The **Human Review** step suspends a workflow and emails somebody for a decision. The run stays paused until they answer, then continues down the branch bound to the outcome they picked. > Dock: Flow · Returns: the reviewer's decision Use it as a checkpoint before anything irreversible, and before acting on AI output nobody has reviewed yet. ## When to Use It - **Financial actions**: approving a refund or a large payment before it processes. - **Publishing**: reviewing AI-drafted posts or replies before they go out. - **Destructive operations**: confirming deletion of records flagged automatically. ## Setting it up Add the step from **Tools** on the canvas dock. Only the first field is required. | Field | Required | Description | | -------------------------- | :------: | --------------------------------------------------------------------------------- | | **Approver Email** | Yes | Who is emailed. One address; a list separated by commas is rejected. | | **Email Subject** | No | Subject line. Defaults to "Action required: approve workflow". | | **Message** | No | Instructions the reviewer reads. Takes references to earlier steps. | | **Context Data** | No | Key and value pairs shown in the email and on the review page. | | **Decisions** | No | The outcomes the reviewer can pick. Defaults to Approve and Deny. | | **Expire after (seconds)** | No | Take the Expired branch if nobody answers in time. Blank or 0 waits indefinitely. | | **Form Title** | No | Heading above the reviewer's form. | | **Form Description** | No | Helper text under the heading. | | **Form Fields** | No | Fields the reviewer fills in alongside their decision. | > **Notifying somebody is not the same as authorising them.** The email goes to > whatever address you enter, but responding requires a signed-in member of this > workflow's team. To collect a decision from a customer or another outside > party, use the [Wait](/build/action-steps/delay) step: its form and webhook > modes accept an answer from outside Glow. ### Giving the reviewer what they need The **Message** is where the decision gets made, so put the values it rests on into it rather than sending a bare "please approve": ```text Refund request from {{ 1.customer_email }} Amount: {{ 1.amount }} Reason given: {{ 2.result }} ``` **Context Data** does the same job in a tidier shape. Each row is a label and a value, and the pairs are listed in the email and on the review page as a small table. A row named `Deal` with the value `{{ 2.dealName }}` reads better than the same reference buried in a paragraph. Both are resolved when the step runs, so the reviewer sees the values as they were at that moment. ### Naming the outcomes **Decisions** is where you set what the reviewer can choose. Leave it empty and they get the default pair, Approve and Deny. Each row you add takes a **Value**, which is how the choice is recorded, and a **Button label**, which is what the reviewer clicks. One row can be flagged as the Expired branch. Every decision gets its own button in the email and its own output on the canvas. "Approve", "Send back for edits" and "Reject" become three branches you wire separately. Connect each output to the steps that should follow that choice. A decision left unwired continues down the step's normal output, and a Deny-style outcome settles the run through its error path. ### Asking for a value alongside the decision **Form Fields** puts a form on the review page. A reviewer can then supply a rejection reason or a corrected amount alongside their decision. Each field takes a **Field name**, which is the key later steps read it by, plus a **Label**, a **Type**, whether it is **Required**, and optional **Help text**. The choice types take a comma-separated list of **Choices**. The types are short text, long text, number, date, dropdown, multi-select, checkbox, radio and file. Answers arrive on the step as `formData`, keyed by the field names you chose. A field named `rejectionReason` is read by a later step as `{{ 5.formData.rejectionReason }}`. ### Setting a deadline **Expire after (seconds)** decides how long the request stays answerable. Leave it blank or at 0 and the request waits indefinitely. Set it, and mark one of your Decisions rows as the Expired branch, and an unanswered request continues down that branch. Set it without marking one and the timeout settles the run through the step's error path rather than being mistaken for approval. ## What the Approver Sees An email with your subject, your message, any Context Data you supplied, and a button per decision. Each link belongs to that one run: once a decision is submitted, the others stop working and the workflow resumes. Approvers away from a desk can answer from a phone browser too, through [Mobile View](/build/the-canvas/mobile-view). Where you configured a form, the button opens the review page with the form on it, and the decision is submitted together with the answers. ## What it passes on While the workflow is paused, the step's output is: ```json { "status": "pending", "approverEmail": "manager@example.com", "subject": "Action required: approve workflow", "message": "Refund request from …", "reviewUrl": "https://…", "context": [{ "key": "Deal", "value": "Acme renewal" }], "decisions": [{ "id": "approved", "label": "Approve" }], "expiresAt": "2026-08-10T09:00:00.000Z" } ``` Glow emails the approver automatically. `reviewUrl` is also published so you can route it elsewhere. Post it to a Slack channel, for example, so whoever is on shift can act. Once somebody answers, the step's own result carries the outcome: | Reference | Contains | | ------------------------- | ------------------------------------------------- | | `{{ N.decision }}` | The Value of the decision that was picked | | `{{ N.formData. }}` | What the reviewer filled in, where you set a form | | `{{ N.answeredAt }}` | When the answer arrived | Where several outcomes must do different things, wiring each decision's own output is the direct route. Reading `{{ N.decision }}` in a [Condition](/build/action-steps/conditions) after the step does the same job in one branch. ## Limits An expiry runs to a maximum of **30 days**. The field refuses a larger number when you type it, so a run cannot end up waiting for a period the step never accepted. Where a decision has to arrive sooner than anyone is likely to check email, set a short expiry and wire the Expired branch to a chaser. Posting the review link into a channel somebody is watching works too. ## Troubleshooting > A workflow waiting for approval shows as **Running** in the execution log. > That is expected. It is suspended, not stuck. **The approver did not get the email.** Check the address in **Approver Email** for typos, and check spam. As a fallback, send `reviewUrl` yourself in a Slack or email step placed after the approval step. **The email was rejected before it went out.** The field takes exactly one well-formed address. Two addresses separated by a comma fail the step rather than emailing both. **The link no longer works.** Each link is valid for a single run, and only until the decision is made. Reopening it afterwards shows that it has already been answered. **Nobody ever responded.** Set **Expire after (seconds)** and wire the Expired branch, so an unanswered request routes somewhere deliberate. A run that is no longer wanted can be cancelled from the **Executions** tab. **Two people answered at once.** Only the first answer counts. The second is recorded as already answered and changes nothing. ## What's Next? - Pause for a time, or until an outside system calls back, with [Wait](/build/action-steps/delay). - Branch on the outcome with [Conditions](/build/action-steps/conditions). - Halt a run deliberately with [Stop and Error](/build/action-steps/stop-and-error). --- Source: https://docs.getglow.ai/build/ai-features/ai-agent # AI Agent > Hand one step of a workflow to an AI that works out its own approach, while the rest of the workflow runs by your rules. The AI Agent step handles work that needs judgement rather than a rule, and that takes more than one move. You give it a goal and the accounts it may use, and it works out the steps itself. That covers reading a company's website, deciding how urgent a message is, or making sense of input nobody formatted. For a single call with no tools (classify this, summarise that) the [AI Prompt](/build/ai-features/ai-prompt) step is simpler and faster.  *The agent's identity and goals are the whole brief. It knows nothing you do not write into these two fields.* The rest of your workflow does not change. Everything before and after the agent still runs by your rules, in the order you drew. The agent is one step where the answer is worked out rather than looked up. Two things define what it can do: 1. **Primary goals**: what you want it to achieve. 2. **Tools**: the accounts it is allowed to act through. It can use nothing else. The AI Agent works in a loop: it reads the goal, picks a tool, reads the result, and repeats until it can answer. ## Setting it up ### Choose the model Open the **Brain (model)** dropdown and pick the model that will drive the agent. It runs on Glow's managed infrastructure, so there is **no API key to configure**. See [Models](/build/ai-features/supported-models) for how to choose. ### Write the Primary goals Say what the agent is for. This can be static text, data from earlier steps, or both. _Example:_ "Research the company that just filled out our lead form, find their most recent product launch, and draft a personalised outreach email." > **The agent sees only the text you put in these fields.** It has no view of > the workflow around it. A reference to an earlier step must be written into > the prompt as a placeholder, such as `{{ 3.ret.company }}`, for the agent to > know that value at all. Nothing arrives implicitly. ### Give it an Identity **Identity** says what the agent _is_, where Primary goals says what it should _do_. For example: "a customer support assistant for an accounting product", or "a research assistant that never speculates". It is optional, and worth filling in whenever the same goal would be handled differently by different kinds of assistant. ### Equip Tools Open **Tools** and switch on the accounts the agent may act through. Each one you turn on becomes a set of actions it can call. A Slack account lets it post messages, and a HubSpot account lets it read and update records.  *Every account is off until you turn it on. The agent can act through the ones switched on here and nothing else.* The model may pick the wrong tool, or reach for one it should leave alone. **Tool Instructions** under Optional Props is where you tell it when to use which. > **The agent cannot reach anything you have not listed here.** It acts through > the same connections the rest of your workflow uses. It cannot create, delete, > or re-authorise one. Tools has two halves: the accounts you switch on here, and the workflows you have already built, listed below them under **Workflows**. Three tools come with every agent as well, whether or not you connect an account: - **Search the web** for something it does not know. - **Run code** to calculate, reshape or check a value. - **Read a URL you give it** — a web page, and also a PDF, an image, or a CSV or JSON file. The address has to be publicly reachable: a link behind a login or inside your private storage returns nothing, so pass the contents in through the prompt instead. ### Hand it a workflow you already built Below the accounts, the Tools panel has a second section headed **Workflows**. It lists the reusable workflows in your workspace that the agent is allowed to run, and each one is switched on with the same toggle. Each workflow you turn on gets its own free-text box, **When should the Agent use this?**, which tells the model when to reach for it. It arrives filled in with `Use this tool to run {name} and return its result.` Replace that with the actual trigger: "Use this when the customer asks about a refund and you need their order history." This is the strongest way to constrain an agent. The judgement stays with the model, and the steps stay yours — drawn on a canvas, tested, and unchanged by whatever the agent decides. Where a rule is fixed, put it in a workflow rather than describing it in the prompt and hoping. ### Shape the behaviour Everything else sits under **Optional Props**. Click **Show more** to see the full set. All of it is prompt material: what you write here reaches the model, and nothing else does. | Field | What it does | | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Background information** | Context the agent should assume: your product, your customers, anything it cannot infer. | | **Dos and Don'ts** | Behaviour it must always or never exhibit. | | **Tool Instructions** | When to reach for which tool, rather than leaving the choice entirely to the model. | | **Safety Constraints** | Hard limits it must not cross. | | **Error Handling** | How to behave when it hits an error or something unexpected. | | **Output Format** | The shape the answer should take. | | **Style** | Tone, voice and register. | | **Examples** | Sample inputs and the outputs you want for them. | | **Step by Step** | A reasoning process to follow, when the order of work matters. | | **Memory Enabled** | Off by default. Turn it on to carry context across runs. Reveals **Memory Instructions**, where you say what is worth remembering. | | **Think Tool Enabled** | A dedicated reasoning pass before answering. **On unless you switch it off.** Better on ambiguous tasks, slower everywhere: turn it off for a narrow, well-specified agent running at volume. | > **Memory belongs to the workflow, not to you.** Every run of this step reads > and writes the same memory, whoever started it. A colleague who runs the > workflow picks up what the agent already learned, and anything it learns on > their run is there on yours. That is the point: an agent that forgot > everything each time somebody else ran it would never get better at the job. > > Two workflows never share memory, even in the same workspace and even when one > is a copy of the other. So a second agent built for a different job starts > clean, and you can keep a sensitive workflow's memory away from the rest. > > Memory belongs to a saved workflow, so a run that is not attached to one — a > step tried out on its own before the workflow exists — remembers nothing and > leaves nothing behind. Save the workflow first if you are testing what the > agent learns. > **Running one workflow for several clients?** The memory follows the workflow, > so every client's runs read and write the same store. That is what you want > for one client's own automation, and not what you want for a workflow you > operate across an entire book of business. > > Give each client their own copy of the workflow, and their agents keep their own > memory. Where a single shared workflow is the right shape, leave **Memory > Enabled** off and pass the context in through the step's fields instead: what a > run needs is then explicit, and nothing carries between clients. #### Output Format and Examples earn their keep These two are what make an agent usable by the rest of your workflow. An agent whose answer is free prose forces the next step to pick it apart. Tell it instead to return `{"decision": "approve"|"reject", "reason": "..."}`, and show it an example of exactly that. You then have something a [Condition](/build/action-steps/conditions) can route on. If you find yourself building fragile text-matching after an agent, the fix is usually these two fields rather than a cleverer step downstream. ## What it passes on The agent hands back two values on the step: | Reference | What it holds | | ------------------ | -------------------------------------------------- | | `{{ N.result }}` | The answer itself | | `{{ N.thoughts }}` | Its reasoning along the way, useful when debugging | Give it an Output Format that asks for JSON and you can reach inside the answer directly: `{{ N.result.urgency }}`. Run the step once and check the Executions tab to confirm the shape before you build on it. > **Run the step once and read the Executions tab before wiring the next one.** > A reference that finds nothing stops the run and names itself in the message, > whether the step number is wrong or the path after it is. Checking the real > path against a run catches both before they cost you a run. ## How the agent reaches your accounts Every account you switch on opens its own connection when the step runs, acting as the same person who authorised it, and closes again when the step finishes. An agent can never reach further than the account you gave it. The agent then asks each connection what it can do, rather than working from a list we ship. Two useful things follow from that. **New actions arrive on their own.** When an app gains an action, your agent can use it. Nothing to update, nothing to re-connect. **The choice is made during the run.** Which action the agent reaches for is decided as it works, not fixed in the settings beforehand. That is what **Tool Instructions** is for: it is where you say which to prefer. You configure none of this. Switching the account on is the whole setup. ## Getting the same fields every time The agent carries the same **Response shape** option as AI Prompt. You will find it in the main form, directly below Primary goals. Describe the structure you want and the agent's answer holds that shape on every run, so a later step can read `{{ 4.result.status }}` and know it is always there. See [Getting the same fields every time](/build/ai-features/ai-prompt#getting-the-same-fields-every-time). ## Approving what the agent does Some actions cannot be taken back: an email to a customer, a refund, a record deleted. Put a [Human Review](/build/action-steps/user-approval) step between the agent and the action. The run pauses, someone approves it from the canvas, and only then does it go ahead. Worth doing whenever being wrong costs more than waiting. ## Recovering from a failed run An account may not connect, a service may be down, or the data may not be there. The agent then stops with an error rather than guessing. You handle a failed agent like any other failed step. Draw an **Error Path** from the step to a fallback, such as a manual review task. The mechanics are ordinary [Error Handling](/build/core-concepts/error-handling); nothing about the agent changes them. > This bounds what happens when the agent's *tools* fail. It does not guarantee > the model's reasoning is correct. For decisions with consequences, put a > [Human Review](/build/action-steps/user-approval) step between the agent and > the irreversible action, as above. ## Limits A workflow the agent calls has two minutes to answer. Keep a called workflow short, or move slow work behind its own trigger so the agent starts it and does not wait for it. See [Calling Another Workflow](/build/action-steps/trigger-workflow) for how subflows are built and what they return. ## Examples to copy Use these configurations as starting points, then adapt the goals, constraints, and output to your workflow. ### Blueprint 1: B2B Company & Prospect Research Equip the agent with the built-in **Search the web** and **Read a URL you give it** tools to perform deep prospect research before a sales outreach. **Identity** ``` A meticulous B2B market intelligence researcher that extracts verified company information and never hallucinates facts. ``` **Primary goals** ``` Research the company with domain "{{ 1.email | extract_domain }}" using web search. Find their primary value proposition, target customer profile, estimated headcount, and any notable product announcement from the last 90 days. Email: {{ 1.email }} Lead Name: {{ 1.name | title_case }} ``` **Output Format** ```json { "company_name": "string", "industry": "string", "value_proposition": "one clear sentence", "target_market": "SMB | Mid-Market | Enterprise", "recent_news": "string or null", "recommended_outreach_angle": "string" } ``` **Dos and Don'ts** ``` Do check the official company website before secondary directories. Don't speculate on revenue or headcount if not publicly stated. If unknown, return null. ``` --- ### Blueprint 2: Customer Support Triage & Sentiment Router Triage incoming customer tickets, verify account tier using your connected CRM, and output clean JSON for downstream [Switch](/build/action-steps/switch) steps. **Identity** ``` A technical customer support triage specialist for a SaaS platform. ``` **Primary goals** ``` Analyze the customer's message: Customer: {{ 1.name }} ({{ 1.email | lower }}) Message: {{ 1.message | strip_html | trim }} 1. Look up the customer's domain ({{ 1.email | extract_domain }}) in HubSpot to verify their tier. 2. Determine urgency and issue classification. 3. Draft a resolution based on the issue description. ``` **Output Format** ```json { "urgency": "Critical | Normal | Low", "category": "Billing | Authentication | Bug | Feature Request | How-To", "tier": "Enterprise | Standard | Free", "sentiment": "Frustrated | Neutral | Delighted", "draft_reply": "string", "requires_human_review": true } ``` **Safety Constraints** ``` Never promise feature timelines, SLA credits, or custom contractual terms. If the customer threatens legal action or churn, set urgency to "Critical" and requires_human_review to true. ``` --- ### Blueprint 3: DevOps Incident & Log Analyzer Equip the agent with your connected **GitHub** and **Jira** accounts to analyze error logs from production webhooks. **Identity** ``` A Site Reliability Engineering (SRE) assistant that diagnoses stack traces and assigns bug severity. ``` **Primary goals** ``` Analyze the following error payload from our APM monitoring webhook: Service: {{ 1.service_name }} Error: {{ 1.error_message }} Stack Trace: {{ 1.stack_trace | truncate:2000 }} 1. Identify the root cause module. 2. Search GitHub for open issues matching the error signature. 3. If an existing issue exists, provide the issue number; otherwise draft a Jira bug summary. ``` **Output Format** ```json { "severity": "P1 | P2 | P3", "root_cause_module": "string", "is_known_bug": true, "jira_summary": "string", "jira_description": "string", "recommended_fix": "string" } ``` --- ## What's Next? - 👉 **[Connect an App →](/manage/apps-and-integrations/connecting-an-app)**: connect the accounts the agent can use as tools. - **[Human Review](/build/action-steps/user-approval)**: require approval before an irreversible action. - **[AI Data Transform](/build/ai-features/ai-transform)**: reshape the agent's answer for later Steps. --- Source: https://docs.getglow.ai/build/ai-features/ai-prompt # AI Prompt Step > Make a single AI call inside a workflow. Write instructions, reference the data you want the model to see, and get a result downstream steps can use. The AI Prompt step makes one AI call and returns the result to the steps that follow. It is the building block for classification, summarisation, extraction, translation, and anything else that fits in a single call. **It runs on Glow's managed infrastructure.** There is no API key to configure, no provider to connect and no model to choose: write the instructions and run it. Usage is billed through your Glow plan. For tasks that need tools, multiple reasoning steps, or memory, use the [AI Agent](/build/ai-features/ai-agent) step instead. To pull several values out of one messy text without writing a prompt, [AI Data Transform](/build/ai-features/ai-transform) does it with one instruction per value. > **The model sees only what you write.** Previous steps are never passed to the > AI automatically. If you do not reference a value in your instructions, it > does not exist as far as the model is concerned. ## Setting it up ### Add it to the canvas Press **a+p**, or drag **AI Prompt** from the **AI** section of the dock. ### Write the instructions Select the step to open the App drawer and fill in the **Instructions** field. ### The fields | Field | Type | Description | | --------------------- | ------- | ----------------------------------------------------------------------------------------------------- | | **Instructions** | String | What the AI should do, including the data it should work with. Required. | | **Response shape** | Editor | Off by default. Name the fields you want back and every run answers with the same fields. | | **Parse JSON result** | Boolean | Off by default. Turn it on when your instructions ask for JSON but you have not set a Response shape. | **Response shape** sits in the main form, directly below Instructions. **Parse JSON result** sits under **Optional Props**. There is no model dropdown. See [Models](/build/ai-features/supported-models) for how the AI steps differ on this. ## Getting the same fields every time A model left to itself will phrase its answer differently from run to run, which is fine for a summary and a problem for anything a later step reads. **Response shape** fixes that: you describe the structure you want, and the model is held to it while it writes rather than checked afterwards. Turning it on opens the shape editor. In the **Fields** view, add one row per value you want back. Name each field, pick its kind, and mark it required or optional. The kinds are text, number, yes/no, list, and group, where a group holds nested fields of its own. If the step has already run successfully, Glow reads the last answer and offers its fields as a starting point. Click **Use this** to accept them, or **Set it up myself** to start from a blank shape. Already have a schema written down? The **JSON** tab takes a JSON Schema directly. There, a shape for a support ticket might read: ```json { "type": "object", "properties": { "category": { "type": "string" }, "urgency": { "type": "string" }, "needsHuman": { "type": "boolean" } }, "required": ["category", "urgency"] } ``` Downstream steps then read `{{ 2.result.category }}` and `{{ 2.result.urgency }}` knowing both are always there. Turning Response shape off keeps the shape you built. Turn it back on and it is there again, so you can pause enforcement without rebuilding anything. **If the answer does not match the shape, the step fails** and says which part was wrong. A malformed answer stops the run rather than travelling on as something a later step misreads. **Ask the AI to fix a wrong shape** changes that. Turn it on and a mismatched answer is sent back once, with the problem described, and the corrected answer is used. It costs one extra AI call on the runs that need it, so the better first move is to describe the shape in your instructions as well. The retry is then a safety net rather than part of the normal path. ## Referencing data Include data from earlier steps by referencing their **step number** inside the instructions. ```text Classify the following support ticket into one of these categories: billing, technical, account, other. Ticket subject: {{ 1.subject }} Ticket body: {{ 1.content }} Respond with only the category name. ``` To pass a step's entire output, reference the step number alone: ```text Summarize this email: {{ 3 }} ``` > **Insert references with the data picker rather than typing them.** The path > depends on the step: an HTTP Request puts its response under `ret`, so it is > `{{ 3.ret.email }}` and not `{{ 3.email }}`. The picker fills in whatever is > correct for that step and shows you the real value beside it. > > A reference that finds nothing (wrong path or wrong step number) stops the > run there and names itself in the error, rather than sending the model a blank. > Run the step once and read the Executions tab to confirm the real path before > you build on it. Full syntax is documented in [Variable Reference Syntax](/reference/variable-syntax). ## What it passes on **`result` keeps the shape of what the AI returned.** Ask for a sentence and it holds a sentence; ask for a list of objects and it holds a list of objects, ready for a later step to walk into or loop over. ```text Extract the following fields from the invoice text below. Return valid JSON with keys: vendor_name, invoice_number, date, total_amount. Invoice text: {{ 1.invoice_text }} ``` A run asking for a joke comes back as: ```json { "result": "Why don't skeletons fight each other? They don't have the guts." } ``` and one asking for three people as: ```json { "result": [ { "name": "Ann", "city": "Prague" }, { "name": "Bob", "city": "Brno" } ] } ``` so `{{ 2.result.vendor_name }}` and `{{ 2.result.0.name }}` both work when the answer has that shape. **When the shape matters, set Response shape.** The answer then always has the fields you named, and later steps can read them with no parsing step. **Parse JSON result** covers the other case: your instructions ask for JSON but you have not set a shape. It tells the model to answer in clean JSON with no markdown around it, and parses the text so later steps can select individual fields. > **The AI Agent returns the same `result`, plus its reasoning.** Swapping one > step for the other does not break `{{ N.result }}`. The agent also > offers `{{ N.thoughts }}`, which the AI Prompt step has no equivalent of. > Ask for the exact keys you want and state that the response must be valid > JSON. Models follow an explicit schema far more reliably than an implied one. ## Limits ### Missing input If a referenced value is empty, that part of the prompt arrives blank and the model works with what remains. That usually produces a confident but wrong answer. Add a [Condition](/build/action-steps/conditions) step before the AI Prompt to check that required data is present. ### The answer arrives in the wrong shape Left to itself a model phrases things differently from one run to the next. Where the shape matters, set **Response shape** and the answer is held to it: see [Getting the same fields every time](#getting-the-same-fields-every-time). ### High-volume runs Every run of the workflow is a model call. Before scaling a workflow to thousands of daily runs, check the effect on both cost and execution time, and see [System Limits](/reference/system-limits) for the ceilings that apply to your plan. ## Examples to copy A webhook receives support tickets. The AI Prompt step classifies each one, and a [Switch](/build/action-steps/switch) step routes it to the right queue. ```text You are a support ticket classifier. Read the ticket below and respond with exactly one category: billing, technical, account, or other. Subject: {{ 1.subject }} Body: {{ 1.content }} ``` A [Scheduler](/build/triggers/scheduler) trigger runs each morning and fetches unread email. The AI Prompt step summarizes each one for a Slack briefing. ```text Summarize the following email in two sentences. Focus on action items and key decisions. From: {{ 2.from }} Subject: {{ 2.subject }} Body: {{ 2.body }} ``` A webhook receives free-text invoices. The step extracts structured fields for a database write. ```text Extract the following fields from the invoice text below. Return valid JSON with keys: vendor_name, invoice_number, date, total_amount. Invoice text: {{ 1.invoice_text }} ``` Set **Response shape** with those four fields so the next step can map each one individually. ## What's Next? - Need tools, memory, or multi-step reasoning? Use the [AI Agent](/build/ai-features/ai-agent) step. - Look up reference syntax in [Variable Reference Syntax](/reference/variable-syntax). --- Source: https://docs.getglow.ai/build/ai-features/ai-transform # AI Data Transform > Pull values out of messy text (an email, a receipt, a transcript) with up to three plain-language instructions. **AI Data Transform** reshapes data into the format you need. Give it up to three plain-language instructions and it hands back one answer per instruction, in the order you wrote them. It is at its most useful on text with no fixed shape, like a customer email, a scanned receipt or a meeting transcript. You do not need to know how the data is formatted underneath.  *Write one instruction per value you want. The step returns an answer for each, in the order you wrote them.* ## AI Data Transform or AI Prompt? Both use Glow's managed models, and both see only the data you reference. AI Prompt makes one model call; AI Data Transform makes one concurrent call per instruction. The other difference is the shape of what comes back: | You want | Use | | ----------------------------------------- | ---------------------------------------------------------------------------------------- | | Up to three ordered values from one text | **AI Data Transform**: one positional answer per instruction | | One answer, worded or structured your way | [AI Prompt](/build/ai-features/ai-prompt): a single result you shape with Response shape | For a single value, either works. AI Data Transform needs less setup; AI Prompt gives you control over the answer's wording and structure. ## Setting it up ### Add the step Open **Tools** in the dock and choose **AI Data Transform**. On the canvas, right-click and pick it under **Data**. ### Write one instruction per value The step has one field: **Instructions**, a list you can add up to three entries to. There is no separate input box. For a fourth value out of the same text, use [AI Prompt](/build/ai-features/ai-prompt) with **Response shape**, which takes one field per value you want back. You point each instruction at the text by step number, the same way you reference a value anywhere else: > Extract the customer's order number from `{{ 2.ret.body }}` The step sees only what your instruction references. Use the data picker to insert the reference rather than typing it: the path depends on which step produced the text. ## What it passes on The step returns one answer per instruction, in the order you wrote them: | Reference | What it holds | | ------------------ | ---------------------------------- | | `{{ N.result.0 }}` | the answer to your 1st instruction | | `{{ N.result.1 }}` | the answer to your 2nd instruction | | `{{ N.result.2 }}` | the answer to your 3rd instruction | Because the answers are positional, reordering your instructions changes what every later reference points at. If you want values you can address by name, ask a single instruction to return JSON and use [Parse JSON](/build/action-steps/parse-json) on the result. ## Limits You can add up to three instructions. For a fourth value from the same text, use [AI Prompt](/build/ai-features/ai-prompt) with **Response shape**, which returns named fields in one result. > How accurate the answers are depends on how clear the source text is. Give it > a smudged scan or an ambiguously worded email, and the answer may be > confidently wrong. > > For anything that costs money or cannot be undone, put a > [Human Review](/build/action-steps/user-approval) step between the extraction > and the action. A person then sees the values before they are used. ## Examples to copy Reach for AI Data Transform when the information you need is in there somewhere but never in the same place twice: - **A receipt or invoice**: instruction 1 asks for the supplier, instruction 2 the total, and instruction 3 the due date. Read them as `{{ N.result.0 }}`, `{{ N.result.1 }}` and `{{ N.result.2 }}`. - **An inbound email**: extract the sender's job title, their company size and what they are asking for. - **A meeting transcript**: extract the owner, the agreed action and its due date. If the text always arrives in exactly the same shape, [Split Text](/build/action-steps/split-text) or [Parse JSON](/build/action-steps/parse-json) will do the job faster and without an AI call. ## What's Next? - Need one shaped answer instead of positional values? [AI Prompt](/build/ai-features/ai-prompt). - Put a reviewer in front of important extractions with [Human Review](/build/action-steps/user-approval). - For work that needs several steps of judgement rather than one extraction, use the [AI Agent](/build/ai-features/ai-agent). --- Source: https://docs.getglow.ai/build/ai-features/choosing-a-step # Choosing an AI Step > What each AI step is good at, what it gives up, and how to tell which one your job needs. Four steps in Glow use AI. Picking the wrong one is the most common reason an automation ends up slower, dearer or less predictable than it needed to be, so this page sets out the trade behind each. The short version: **most jobs are an AI Prompt**. Reach past it only when you can say what the prompt cannot do. ## Start here Answer these in order and stop at the first yes. | Ask yourself | If yes | | -------------------------------------------------------------------- | ----------------------------------------------------------- | | Am I trying to build the workflow itself, rather than run something? | [Workflow Assistant](/build/ai-features/workflow-assistant) | | Do I need up to three ordered values from one piece of text? | [AI Data Transform](/build/ai-features/ai-transform) | | Does it have to act in another app, or decide its own next move? | [AI Agent](/build/ai-features/ai-agent) | | Anything else | [AI Prompt](/build/ai-features/ai-prompt) | ## AI Prompt **One question in, one answer out.** You write what you want in plain language, reference the data it should use, and the step returns a single result. **What it is good at** - **Text work.** Classifying, summarising, rewriting, drafting, translating. - **Volume.** One model call, so it is the cheapest and fastest of the four. - **Repeatability.** The same input gives the same shape of answer. - **Structured output.** Describe the shape and a later step reads `{{ 2.result.status }}` knowing it is there. **What you give up** - **It cannot act.** No sending, no writing to a CRM, no fetching a page. - **No second attempt.** It cannot check its own work or try another approach. - **It sees only what you reference.** An earlier step's value has to be written in as a placeholder. **Reach for it when** the job is one judgement on one piece of text. That covers far more than people expect. ## AI Agent **A goal in, and it works out the steps.** You say what you want achieved and switch on the accounts it may act through. It decides its own next move, uses tools in sequence, and stops when it judges the goal met. **What it is good at** - **Unplannable work.** Research a company, triage against several sources, chase a thread. - **Acting in other systems**, through the accounts you connect. - **Several tools in one step.** Search the web, read a page, calculate, write a record. - **Context across runs**, if you turn Memory on. **What you give up** - **Predictability.** Two runs on the same input can take different routes. - **A forecastable bill.** It is the dearest of the four and the hardest to estimate. - **Traceability.** More moves means more places to go astray, so put [Human Review](/build/action-steps/user-approval) in front of anything irreversible. **Reach for it when** you cannot write down the steps yourself, or when the work has to touch another system to finish. > **The most common mistake is starting here.** An agent looks like the powerful > choice, so it gets picked for jobs a prompt would do more cheaply and more > predictably. If you can describe the steps, you do not need something that > works them out. ## AI Data Transform **One text in, up to three ordered values out.** Write one instruction per value — "the customer's full name", then "the plan they asked about" — and read them by position as `{{ N.result.0 }}`, `{{ N.result.1 }}` and `{{ N.result.2 }}`. **What it is good at** - **Values out of mess.** A raw email, form or document becomes an ordered result you can use downstream. - **No prompt to write.** One line per value, and no JSON shape to describe. - **Speed.** The instructions run at the same time, so three is not three times the wait. **What you give up** - **Three instructions at most.** For a fourth value, use AI Prompt with a response shape. - **No control over wording.** It returns the value, not a sentence you styled. - **It is not in the AI group.** Find it under **Tools → Data → Change**. **Reach for it when** you want fields rather than prose, and three of them will do. ## Workflow Assistant **Describe the automation and it builds it.** This one is different in kind: it does not run inside a workflow, it writes one. **What it is good at** - **A working draft in minutes**, from nothing. - **The fiddly configuration.** Field mappings, channel names, references between steps. - **Explaining a canvas** somebody else built. - **Working beside you.** The canvas stays yours while it builds. **What you give up** - **A draft, not a finished workflow.** Read what it made before you publish. - **Precision.** It works from what you wrote, so a vague request gets a vague skeleton. **Reach for it when** you are starting out, inheriting a canvas, or facing a screen of fields you would rather not fill in by hand. ## The comparison, side by side | | AI Prompt | AI Agent | AI Data Transform | | --------------------- | ----------- | --------------------- | ------------------- | | **Model calls** | One per run | As many as it decides | One per instruction | | **Cost** | Lowest | Highest | Low | | **Predictable** | Yes | Less so | Yes | | **Acts in your apps** | No | Yes | No | | **Picks its model** | No | Yes | No | | **Remembers** | No | Optional | No | | **Main limit** | One answer | Time and cost vary | Three instructions | ## Two habits worth having **Start simpler than you think you need.** Try the prompt first. If it gets you eighty per cent of the way, the remaining twenty is usually a better prompt rather than a bigger step. **Test before you trust.** Run the step on five awkward real cases: the email with no signature, the form where somebody typed "n/a". Read the output rather than the green tick. See [Try it before you trust it](/build/ai-features/overview#try-it-before-you-trust-it). ## What's Next? - [AI Prompt](/build/ai-features/ai-prompt) — write your first one and see how far it gets you. - [AI Agent](/build/ai-features/ai-agent) — hand over a goal and the accounts to reach it. - [Models](/build/ai-features/supported-models) — pick between Flash and Pro once you know which step you need. --- Source: https://docs.getglow.ai/build/ai-features/overview # AI & Agents > The four AI steps, what each one decides, and why none of them needs an API key. Glow's AI steps run on Gemini models, hosted and paid for by us. There is no API key to find and no contract to sign, so a step works the first time you drop it on the canvas. Prefer your own provider? The [AI connectors](#two-ways-to-run-ai-in-a-workflow) in the Apps menu take your key instead. Four steps use AI, and they differ in how much you hand over. - [Workflow Assistant](/build/ai-features/workflow-assistant): Describe the automation you want and it builds the workflow for you. - [AI Prompt](/build/ai-features/ai-prompt): One question, one answer. Classify, summarise or rewrite. - [AI Data Transform](/build/ai-features/ai-transform): Pull ordered values out of messy input, one instruction per value. - [AI Agent](/build/ai-features/ai-agent): Give it a goal and the accounts it may use, and it works out the steps. ## Which one to reach for The question is how much judgement the task needs, and whether the answer takes more than one move. | If you want to | Use | | ---------------------------------------------------------- | ----------------------------------------------------------- | | Build the workflow itself | [Workflow Assistant](/build/ai-features/workflow-assistant) | | Ask one question and use the answer | [AI Prompt](/build/ai-features/ai-prompt) | | Get up to three ordered values from one piece of text | [AI Data Transform](/build/ai-features/ai-transform) | | Let it decide its own next move, and act through your apps | [AI Agent](/build/ai-features/ai-agent) | One step gets mistaken for an AI step because of its name. [Summarize](/build/action-steps/summarize) groups a list and works out a count, total or average for each group. It is arithmetic, not a model. To summarise a piece of writing, use AI Prompt. ### AI Prompt or AI Agent? This is the choice that matters. Both take instructions in plain language, and they differ in how much you hand over. | | [AI Prompt](/build/ai-features/ai-prompt) | [AI Agent](/build/ai-features/ai-agent) | | ----------------------- | ----------------------------------------- | ----------------------------------------------------------------------------------- | | **What it does** | One question in, one answer out | A goal in, and it works out the steps | | **Model calls** | One per run | As many as it decides it needs | | **Speed and cost** | Lowest | Highest, and hardest to predict | | **Same input, twice** | Same shape of answer | Can take a different route each time | | **Can act in an app** | No | Yes, through the accounts you switch on | | **Pick the model** | No | Yes, **Brain (model)** | | **Remembers** | Nothing between runs | Across runs, if you turn Memory on | | **Ceiling to plan for** | None beyond the run's own limits | Bounded by your plan's step timeout — see [System Limits](/reference/system-limits) | Start with AI Prompt. It is one call, it is quick, and the answer comes back the same shape every time. Move to the agent when the work genuinely takes several moves that cannot be planned in advance, or when it has to act in another system to finish. A prompt that works is easier to fix in six months than an agent that mostly works. [Choosing an AI Step](/build/ai-features/choosing-a-step) sets out what each one gives up in exchange for what it is good at. ### And AI Data Transform, for pulling out values [AI Data Transform](/build/ai-features/ai-transform) is narrower than either. Write one instruction per value you want — "the customer's full name", then "the plan they asked about" — and it returns an ordered list. Read the first as `{{ N.result.0 }}`, the second as `{{ N.result.1 }}` and the third as `{{ N.result.2 }}`. Use it when a messy email or form has to become a few values. It takes **three instructions at most**, making one model call per instruction at the same time rather than one after another. For a fourth value, AI Prompt with a described response shape does the same job. Find it in the dock under **Tools → Data → Change**, not in the AI group. ### AI inside a routing step Two routing steps can use a model for a single rule without being AI steps themselves. In [Conditions](/build/action-steps/conditions#writing-an-ai-condition) and [Filter](/build/action-steps/filter-items#describing-a-rule-in-words), a rule's type can be set to **AI Condition** and the test written in words: "the message sounds like a complaint". Reach for it only where a comparison cannot express the test. It costs a model call each time it is evaluated: in Filter, that is once per item in the list. It is also a judgement rather than a comparison, so two runs on the same input can differ. [Switch](/build/action-steps/switch) is typed-only. ## Two ways to run AI in a workflow The four steps above are Glow's own. There is also an **AI** group in the Apps menu holding connectors for the major providers, which you use with your own account. Both can sit in the same workflow. | | Glow's AI steps | AI app connectors | | ----------- | --------------------------- | ------------------------------ | | **Model** | Gemini, chosen by us | Whatever your provider offers | | **API key** | None | Yours | | **Billing** | Your Glow plan | Your account with the provider | | **Setup** | Drop the step on the canvas | Connect the account first | Use Glow's own steps unless you have a reason not to: nothing to set up, and one bill. Reach for a connector when you already pay for a provider, or when a particular model is written into how your team works. ### The providers you can connect **OpenAI (ChatGPT)**, **Anthropic (Claude)**, **Google Gemini**, **Mistral AI**, **xAI**, **DeepSeek**, **Cohere**, **Llama AI** and **Ollama** all appear under **AI** in the Apps menu. Each one authenticates with an API key you paste in once, the same as any other app connection. Usage is billed to your account with that provider and covered by your agreement with them. Keys are stored encrypted. See [Credentials](/manage/apps-and-integrations/credentials). ## Choosing a model Among Glow's own steps, the **AI Agent** is the only one that lets you pick a model, from the **Brain (model)** dropdown in its settings. The rest run on a managed default. Model names change with each generation, but they sort into families whose trade-off does not move. **Flash** is faster and cheaper; **Pro** reasons better over long or ambiguous input. A new agent starts on Pro, which is the safe choice rather than the cheap one. See [Models](/build/ai-features/supported-models) for which to use where. ## What a run actually uses An AI step is charged as **model calls**, and the count is something you can read off the workflow before you build it. - **AI Prompt** makes one call per run. - **AI Data Transform** makes one per instruction, so three instructions is three calls, made at the same time. - **An AI Condition** rule makes one call each time it is evaluated. Inside [Filter](/build/action-steps/filter-items#describing-a-rule-in-words) that is once per item, so a hundred-item list is a hundred calls. - **The AI Agent** makes as many as it decides it needs, and that number varies between runs on the same input. Volume multiplies all of it. A workflow running a thousand times a day makes the AI Prompt / AI Agent choice a thousand times over. Measuring one representative run before you go live is worth the ten minutes. See [System Limits](/reference/system-limits) for the ceilings on your plan. ## Try it before you trust it A model gives you an answer whether or not it had enough to work with, so the useful question is never "does it run" but "is it right on the cases I care about". ### Run the step on its own Select the step and open **Test & Debug**. It runs without the rest of the workflow, so you can iterate on the wording without waiting for a trigger. ### Feed it your awkward cases Not the tidy example — the email with no signature, the form where somebody typed "n/a", the invoice in a second language. Five real cases tell you more than fifty clean ones. ### Read the output, not the tick A step that shows green stored _something_. Open the result and check the values are the ones a later step will need, in the shape it expects. ### Then wire up what comes after Once the answer is stable, connect the steps that read it. Building downstream on an unstable answer means changing both later. > **Describe the shape you want and the guessing stops.** Both AI Prompt and the > AI Agent take a **Response shape**, which turns free prose into named fields a > [Condition](/build/action-steps/conditions) can route on. It is the single > biggest thing you can do to make an AI step behave like the rest of your > workflow. ## The wider AI catalogue Glow's own steps are the start, not the limit. The app catalogue carries hundreds of AI services you connect the same way as Gmail or Slack, and a workflow can chain them: read a document with one, reason over it with a Glow step, store the result in another. | To do this | Connect one of | | ------------------------------------------- | -------------------------------------------------------------------------------------------- | | **Semantic search over your own documents** | Pinecone, Weaviate, Qdrant, Milvus | | **Turn speech into text** | AssemblyAI, Trint, IBM Cloud Speech to Text | | **Turn text into speech** | ElevenLabs, Amazon Polly, Voicemaker | | **Pull fields out of a scan or a PDF** | Mindee, PDF.co, OCR.space | | **Search the web for an answer** | Perplexity, Exa, Tavily | | **Generate an image or a video** | Runway, DreamStudio, Leonardo AI, HeyGen | | **Run an open-source model** | Hugging Face, Replicate | | **Call a provider with your own key** | OpenAI, Anthropic, Mistral and the rest of the [AI group](#two-ways-to-run-ai-in-a-workflow) | Search the **[Integrations Directory](https://getglow.ai/integrations)** for the service you already use — it lists every trigger and action Glow supports for each one. ### Retrieval over your own documents A vector database is the piece that makes "ask a question, get the right passage back" work, and Glow connects to the ones people already run. The shape is two workflows rather than one setting: ### Index what you have A workflow triggered by a new file reads its text, sends it to your embedding provider, and writes the vector to Pinecone, Weaviate, Qdrant or whichever you use. ### Query it when you need an answer A second workflow embeds the incoming question, queries the same database for the closest passages, and hands those to an [AI Prompt](/build/ai-features/ai-prompt) or [AI Agent](/build/ai-features/ai-agent) as context. The reasoning is Glow's; the storage is yours, in a service you control and pay for directly. For a document that is simply on the web, none of that is needed: point an [AI Agent](/build/ai-features/ai-agent) at the URL and it reads what is there, PDFs and images included, as long as the address is publicly reachable. ## What AI steps cannot see An AI step reads only what you write into its fields. It has no view of the workflow around it, and nothing reaches it implicitly: a value from an earlier step has to be written in as a placeholder, such as `{{ 3.ret.company }}`. That is worth knowing before you debug an answer that looks wrong. Nine times out of ten the model never had the information. ## What's Next? - Start with a single call in [AI Prompt](/build/ai-features/ai-prompt). - Hand a multi-step job to the [AI Agent](/build/ai-features/ai-agent). - Pick the right model for the job in [Models](/build/ai-features/supported-models). --- Source: https://docs.getglow.ai/build/ai-features/supported-models # Models > Which models power Glow's AI steps, how to choose between them, and why you do not need an API key to get started. Glow's AI steps run on Gemini models, on infrastructure we host and pay for. You do not supply an API key, manage rate limits, or hold a contract with a model provider. That is handled for you, and usage is billed through your Glow plan. ## Which step chooses a model Only one of the two AI steps lets you pick a model. | Step | Model selection | | ----------------------------------------- | ------------------------------------------------------------ | | [AI Prompt](/build/ai-features/ai-prompt) | None. Runs on a managed default tuned for single-call tasks. | | [AI Agent](/build/ai-features/ai-agent) | **Brain (model)** dropdown in the App drawer. | [AI Data Transform](/build/ai-features/ai-transform) runs on the same managed default as AI Prompt. If you want control over the model, use the AI Agent step. > The field is labelled **Brain (model)** on the canvas. New models appear in > the dropdown as they ship, so it always shows the current list. ## Choosing a model Model names change by generation, but they sort into three families and the trade-off between them does not move: **Flash** is faster and cheaper. Use it for classification, routing, extraction, short summaries, and anything running at high volume. **Pro** gives stronger reasoning over long or ambiguous input. Use it for multi-step agent work, complex tool use, careful document analysis, and drafting. **Flash Lite** variants trade further capability for speed and cost. They suit narrow, well-specified tasks such as single-label classification. A new AI Agent step starts on a **Pro** model, which is the safe default rather than the cheap one. For classification, routing and extraction, switch it to Flash and check the output against a handful of real cases. On a high-volume workflow that is usually the single biggest saving available. Move back to Pro when you can point at a specific output the smaller model got wrong. ### By Task | Task | Start with | | --------------------------------------- | ---------- | | Classification, routing, tagging | Flash Lite | | Summarization, field extraction | Flash | | Multi-step agent reasoning and tool use | Pro | | Long or unstructured document analysis | Pro | ### By Volume High-volume workflows amplify both cost and latency. A workflow running 1,000 times a day makes the Flash/Pro choice roughly a thousand times over. Measure on a representative sample before committing a Pro model to a hot path. ## Setting it up 1. Select the **AI Agent** step on the canvas to open the App drawer. 2. Open the **Brain (model)** dropdown. 3. Pick a model. Different steps in the same workflow can use different models. A common pattern is a cheap model for an initial triage decision and a stronger one only on the branch that needs it. ## API keys You do not need one. Every Glow AI step runs on managed infrastructure, and there is no key to configure before your first workflow. If you would rather use your own provider, the Apps menu carries connectors for [OpenAI, Anthropic, Mistral and the rest](/build/ai-features/overview#the-providers-you-can-connect). Those take your key and bill to your account with that provider. ## What's Next? - Write single-call prompts with the [AI Prompt](/build/ai-features/ai-prompt) step. - Build autonomous, tool-using logic with the [AI Agent](/build/ai-features/ai-agent) step. --- Source: https://docs.getglow.ai/build/ai-features/workflow-assistant # Workflow Assistant > The Workflow Assistant builds and changes workflows from a plain-language description, without locking your canvas while it works. The **Workflow Assistant** builds and changes workflows for you. Describe what you want in plain language and it places the steps, joins them up, and fills in their settings. It works on an empty canvas and on one that already has a workflow on it, including one a colleague built.  *The assistant reports each step as it places it, and finishes by saying what it built and what you still need to change.* ## It works beside you, not instead of you The assistant runs alongside the canvas rather than taking it over. Building takes a minute or two — it reads each app's documentation before filling in a step — and **the canvas stays yours the whole time**. Drag a step, edit settings, draw a connection, start a test run. None of it waits for the assistant to finish. That holds for your teammates too. The assistant works through the same live canvas everyone else is on, so a colleague watches the steps appear as they are placed. What it does while you carry on: | | | | ---------------------------------------- | ------------------------------------------------------------------------------------------------- | | **Places and connects steps** | Trigger, actions and the connections between them, in the order the work happens. | | **Fills in the settings** | Channel names, field mappings, references to earlier steps. Ready to run, not ready to configure. | | **Reads the app's documentation first** | Which is why it takes a minute, and why the fields come out right. | | **Handles an unconnected app** | It asks you to link the account and keeps building everything else meanwhile. | | **Leaves sticky notes** | Your prompt stays on the canvas, so the next person sees what was asked for. | | **Explains a workflow it did not build** | Point it at an inherited canvas and ask how the routing works. | ## The conversation is shared The assistant thread belongs to the workflow, not to you. Everyone on the team who can open that workflow sees the same conversation, including prompts your colleagues typed. Each message shows who wrote it, and you can see when someone else is composing one. The panel behaves like the rest of the multiplayer canvas. > Treat what you type here as visible to your teammates. If a workflow is set to > **Me only**, its conversation is private to you; anything shared with the team > is not. While the assistant builds, its progress updates collapse into a single line you can expand. Questions it asks you, authorization requests, and its final answer always stay visible on their own. They are never folded away. --- ## Building from scratch Describe the automation you want and the assistant builds a first draft. It places the trigger and the steps, joins them up, and fills in the settings it can work out. You review it and change what you want before going live. ### Open the Assistant The assistant opens on its own with every new workflow. On one you already have, click **Assistant** in the panel at the bottom right of the canvas. ### Prompt your idea Type a natural-language description and press **Enter**. > _"When a new row is added to my Google Sheet, extract the email address, use AI to draft a welcome email, wait 2 hours, and then send it via Gmail."_ ### Watch it build The assistant places the trigger and the steps, joins them up, and fills in the settings it can work out. That prompt produces four connected steps: a Google Sheets trigger, an AI step to draft the email, a two-hour Wait, and a Gmail step to send it. It also leaves your prompt on the canvas as a sticky note, so the next person can see what was asked for. ### Answer anything it asks Where the request leaves room for interpretation, the assistant asks rather than guessing. "When a message arrives" could mean Slack, Gmail or a webhook, so it will ask which you meant before building. ## Modifying and explaining a workflow The assistant can read a workflow that already exists and explain it, change it, or suggest what to do next. That is useful on one you inherited. Open a workflow that already has steps and the box asks **"What should we change?"** rather than what to automate. Open a workflow built by a colleague and you may not understand how it works. Ask the assistant: *"Explain how this routing logic handles failed payments."* It reads the entire canvas context and summarizes the logic for you. You can prompt the assistant to rewire existing steps. Say: *"Add a Slack notification if the 'Check Status' condition fails."* The assistant knows exactly which condition step you mean. It splices in the new action without destroying your manual work. Ask the assistant to review your workflow for edge cases. It might suggest adding a loop for bulk processing, or placing a Human Review step before a risky database update. ## It configures the steps, not just places them The assistant does not drop a step on the canvas and leave the settings to you. ### It fills in the fields Ask it to _"send a message to `#sales` and mention the person from the webhook"_. It places the Slack step, sets the channel to `#sales`, and points the message text at the person's name from the trigger. The step is ready to run rather than ready to configure. ### When the app is not connected yet Ask it to add a row to Google Sheets before you have connected Google, and it does not guess or leave a broken step behind. A card appears in the chat naming the app and the action it needs (_"To use New Row Added (Instant) in this workflow, please authorize your account"_), with a **Link account** button. It keeps building everything else in the meantime, so one unconnected app does not hold up the rest of the workflow. Connect the account and that step is ready. --- ## Sketch the plan in sticky notes, then ask for it The canvas takes sticky notes (`N`), and the assistant reads what they say. That turns a planning session into a prompt. Sketch each stage of the process as its own note: "leads arrive by webhook", "score them with AI", "route hot ones to #sales". Then ask the assistant to _"build the workflow described in my sticky notes."_ Arrows between notes are for the people reading the sketch: draw them to show the order of the plan. The assistant works from the notes' text, so put the sequence in words too, by numbering the notes or saying in each what follows. Notes never run and never hold data, so the sketch can stay on the canvas beside the finished workflow as its documentation. See [The Dock & App drawer](/build/the-canvas/the-dock) for what else the canvas takes besides steps. ## Prompt Library for Common Scenarios Use these proven prompt patterns to build, refactor, and audit workflows quickly: ### 1. Inbound Lead Qualification & Routing ``` "When a new Typeform submission arrives, extract the email domain using data transformation, look up the organization in Apollo, and add a Condition: if employees > 500 route to Salesforce Enterprise AE queue, otherwise send a nurture email via Gmail and log to Airtable." ``` ### 2. Multi-Channel Support & Incident Escalation ``` "Create a workflow triggered by Zendesk New Ticket. Add an AI Prompt step to classify sentiment as Positive, Neutral, or Angry. If Angry, post an urgent alert to the #support-escalations Slack channel with the ticket URL; otherwise update the Zendesk ticket tags." ``` ### 3. Automated Document Processing with Human Review ``` "Trigger on new Gmail email with PDF attachment. Add an AI Agent to extract vendor name, invoice date, line items, and total amount as JSON. Insert a Human Review step displaying the extracted total, followed by a loop that inserts each line item into our Notion database." ``` ### 4. Splicing Error Handlers & Fallbacks ``` "Add an Error Path to the 'HubSpot: Create Contact' step. If it fails, send a notification to #ops-alerts in Slack with the error message and the failed email payload." ``` --- ## Best practices **Ask for the shape first, then the detail.** Try "When a Typeform response arrives, add the person to HubSpot and post to #sales". That gets you a working skeleton faster than one prompt carrying every rule you have. Once it is on the canvas, ask for one change at a time: _"make the Slack message include their company name."_ **Hand it the fiddly configuration.** Draw the business logic yourself if you prefer having decided it. Then ask the assistant to fill in a step's fields, or to point each value from a webhook at the right field in your CRM. That is the part it saves you most on. **Nothing it does is permanent.** If you don't like what it built, `Cmd+Z` reverts it. The whole generated branch goes in one press. Your undo history stays yours: it never reaches into a teammate's work. See [Undo & Redo](/build/the-canvas/undo-redo). ## What's Next? The assistant builds a draft, not a finished workflow. Read what it produced before you publish, particularly any step that sends something outward. - 👉 **[Quickstart: Your First Workflow →](/getting-started/tutorials/your-first-workflow)**: build a workflow by hand so you can assess what the Assistant drafts for you. - **[Choosing an AI Step](/build/ai-features/choosing-a-step)**: decide whether the workflow needs AI Prompt, AI Data Transform or AI Agent. - **[Undo & Redo](/build/the-canvas/undo-redo)**: understand how assisted changes fit into shared canvas history. --- Source: https://docs.getglow.ai/build/core-concepts/data-transformation # Data Transformation > Format text, calculate numbers, parse dates, and reshape lists directly inside your step fields without adding extra canvas steps. When building workflows, incoming data is rarely in the exact format your downstream apps expect. Customer names might have accidental spaces, prices might lack tax or currency symbols, and timestamps might be in UTC instead of your local timezone. Instead of cluttering your canvas with intermediate code or formatting steps, **Data transformation** lets you clean, calculate, and reshape data **directly inside any step field**. --- ## The Core Concept Whenever you reference data from an earlier step (like `{{ 2.email }}` or `{{ 3.total }}`), you can attach a chain of transformations to it. The transformations execute in-memory right when the step reads the token, without creating intermediate canvas steps or spending extra step credits. --- ## How to Use Data Transformation You can configure transformations visually or write them inline using pipe syntax. ### 1. Visual Configuration (Workflow Data Panel) You can configure transformations visually without writing formulas or syntax: ### Open the Workflow Data Panel Click into any step input field on your canvas to open the **Workflow data** drawer/modal (where you select dynamic variables). ### Switch to the Data Transformation Tab In the Workflow data top bar, click the **Data transformation** tab (next to **Data select** and **Data flow**). ### Add Transformations to Step Variables All variables mapped in the current step appear here. Click **Add transformation** on any variable to add operations (such as **Trim spaces**, **To lowercase**, **Times**, **Format as currency**, or **Format the date**). ### Live In-Memory Execution The transformations run in memory when this step resolves the field. The transformed value is used in that field, while the earlier step's stored output stays unchanged. If later steps need the changed value as a reusable result, use a dedicated canvas step instead. ### 2. Fast Inline Pipe Syntax If you prefer writing directly inside text inputs, append a pipe (`|`) and the operation name inside any variable token: ```text {{ 2.email | trim | lower }} {{ 3.subtotal | times:1.21 | format_currency:"$" }} {{ 1.created_at | format_date:date_medium:Europe/Prague }} ``` --- ## Everyday Examples | Common Goal | Original Data | Transformation | Result | | :------------------------------- | :----------------------- | :---------------------------------------- | :------------------- | | **Clean up customer name** | `" john doe "` | `Trim spaces` + `Capitalise Each Word` | `"John Doe"` | | **Extract email domain** | `"alex@acme.com"` | `Take the domain` | `"acme.com"` | | **Add 21% VAT and format price** | `100` | `Times: 1.21` + `Format as currency: "$"` | `"$121.00"` | | **Format phone number** | `"777123456"` | `Format a phone number: CZ` | `"+420 777 123 456"` | | **Human-readable date** | `"2026-09-01T12:00:00Z"` | `Format the date: 13 Aug 2026` | `"1 Sep 2026"` | | **Join list into text** | `["Apple", "Banana"]` | `Join items (comma + space)` | `"Apple, Banana"` | | **Fallback for missing value** | `null` or `""` | `If empty, use: "Unknown"` | `"Unknown"` | --- ## Operation Catalogue ### Text & Clean Up Operations Use these operations to clean user input, change capitalization, slice text, and extract specific tokens. | Operation | Inline Syntax | Description | Example | | :---------------------------- | :----------------------- | :----------------------------------------------------------------------------------- | :-------------------------------------------------- | | **Trim spaces** | `\| trim` | Removes leading and trailing whitespace. | `" hello "` → `"hello"` | | **lower case** | `\| lower` | Converts all letters to lowercase. | `"Hello World"` → `"hello world"` | | **UPPER CASE** | `\| upper` | Converts all letters to uppercase. | `"Hello World"` → `"HELLO WORLD"` | | **Capitalise Each Word** | `\| capitalize` | Capitalizes first letter of each word; preserves hyphens. | `"john doe"` → `"John Doe"` | | **Take the text out of HTML** | `\| strip_html` | Removes HTML tags while preserving line breaks and converting list items to bullets. | `"Hi"` → `"Hi"` | | **Take the markdown out** | `\| strip_markdown` | Removes markdown headings, bolding, and links for plain text messaging. | `"**Important**"` → `"Important"` | | **Remove the code fence** | `\| strip_fence` | Unwraps outer ` ``` ` markdown code blocks from AI outputs. | `"```json\n{}\n```"` → `"{}"` | | **Replace text** | `\| replace:"old":"new"` | Replaces all occurrences of a search string. | `"cat" \| replace:"cat":"dog"` → `"dog"` | | **Everything before** | `\| before:"@"` | Extracts text before the first occurrence of a delimiter. | `"user@glow.app"` → `"user"` | | **Everything after** | `\| after:"@"` | Extracts text after the first occurrence of a delimiter. | `"user@glow.app"` → `"glow.app"` | | **First line only** | `\| first_line` | Takes only the first non-empty line of text. | `"Line 1\nLine 2"` → `"Line 1"` | | **Shorten to** | `\| limit:100` | Truncates text after a specified character count. | `"Long text" \| limit:4` → `"Long"` | | **Split on** | `\| split:comma` | Splits text into a true list (by comma, space, newline, or tab). | `"a, b" \| split:comma` → `["a", "b"]` | | **Take the domain** | `\| extract_domain` | Extracts clean domain from URL or email (removes `www.` and protocols). | `"https://glow.app"` → `"glow.app"` | | **Take the email address** | `\| extract_email` | Finds and extracts the first valid email address from raw text. | `"Contact support@glow.app"` → `"support@glow.app"` | | **Take the number** | `\| extract_number` | Extracts first numeric sequence (preserves leading zeros for SKUs). | `"Order #0042"` → `"0042"` | | **Take the link** | `\| extract_url` | Finds and extracts the first web address from text. | `"Visit https://glow.app"` → `"https://glow.app"` | | **Make safe for JSON** | `\| escape_json` | Escapes quotes and newlines so text can safely sit inside raw JSON. | `"A \"quote\""` → `"A \\\"quote\\\""` | | **Make safe for web address** | `\| url_encode` | Encodes special characters for URL parameters. | `"Hello World"` → `"Hello%20World"` | | **Count characters / words** | `\| count_characters` | Returns the total character or word count as a number. | `"Hello"` → `5` |We rotated our Okta cert and our team is locked out. Urgent help needed!
" } ``` ### 2. The AI Agent: Triage and Solution Drafting Add an **AI Agent** step. 1. **Brain (Model):** Select `Gemini 2.5 Flash` for fast, cost-effective reasoning. 2. **Tools:** Turn on **Search the web** (or a workflow tool that searches your internal docs) and toggle on your **HubSpot** account. 3. **Primary goals:** Write instructions that leverage in-field token transformations to sanitize the input: ```markdown You are an expert technical support engineer for our SaaS platform. Customer: {{ 1.name | trim | title_case }} Email: {{ 1.email | trim | lower }} Company Domain: {{ 1.email | extract_domain }} Subject: {{ 1.subject | trim }} Issue: {{ 1.message | strip_html | trim }} Instructions: 1. Use the HubSpot tool to check if the company domain belongs to an Enterprise or VIP customer tier. 2. Search the knowledge base for resolutions matching the subject and issue. 3. Classify urgency: "Critical" if Enterprise customer or system outage; otherwise "Normal". 4. Draft a courteous, accurate response. 5. Return a strict JSON object with: - "urgency": "Critical" | "Normal" - "tier": "Enterprise" | "Standard" | "Unknown" - "category": "Authentication" | "Billing" | "Bug" | "General" - "draft_reply": "string" - "summary": "one-sentence summary of issue" ``` 4. Toggle on **Parse JSON result** in the agent settings so downstream steps can read `{{ 2.result.urgency }}` directly. ### 3. The Logic: Switch Step Add a **Switch** step after the agent to determine the handling path: - **Case 1 (Escalate):** `{{ 2.result.urgency }}` equals `Critical` OR `{{ 2.result.tier }}` equals `Enterprise`. - **Fallback (Automated Reply):** Standard tickets that do not require immediate on-call escalation. ### 4. Branch A: Urgent Escalation (Slack + Zendesk) On the **Case 1 (Escalate)** route, connect two parallel or sequential actions: 1. **Slack: Send Message** - Channel: `#urgent-support` - Message: ```markdown 🚨 _VIP Customer Incident Escalated!_ • _Customer:_ {{ 1.name | title_case }} ({{ 1.email | lower }}) • _Company:_ {{ 1.email | extract_domain }} [Tier: {{ 2.result.tier }}] • _Category:_ {{ 2.result.category }} | _Urgency:_ {{ 2.result.urgency }} • _Summary:_ {{ 2.result.summary }} _AI Suggested Resolution:_ > {{ 2.result.draft_reply }} ``` 2. **Zendesk: Create Urgent Ticket** - Priority: `Urgent` - Tags: `vip`, `ai-triaged`, `{{ 2.result.category | lower }}` - Internal Note: Include `{{ 2.result.draft_reply }}` so the assigned engineer can review and send with one click. ### 5. Branch B: Standard Resolution On the **Fallback** route: 1. **Zendesk: Create & Respond** - Status: `Pending` - Public Reply: `{{ 2.result.draft_reply }}` - Tags: `ai-automated-reply`, `{{ 2.result.category | lower }}` > **Human in the Loop:** For sensitive categories (such as Billing refunds), > place a **[Human Review](/build/action-steps/user-approval)** step before > sending the public reply so an agent can review the draft before it reaches > the customer. ## Test and go live ### Send the sample payload Use the Webhook trigger's test URL and send the JSON above. Confirm the agent's stored `result` contains all five fields from the requested response shape before you wire the Switch or either destination. ### Prove both routes First use an urgent outage message and confirm **Escalate** creates the urgent ticket and Slack alert. Then change the message to a routine question and confirm **Fallback** creates the standard pending response without notifying the urgent channel. ### Review what the agent used Read the agent execution and the records it found through its tools. A successful step means it returned an answer; verify the account tier, category and suggested resolution against the source records before allowing an automatic public reply. ### Set the workflow Live Switch the workflow to **Live**, send one controlled request to the Live webhook URL and confirm one ticket reaches the intended branch. Keep Human Review in front of public replies until your test set consistently produces acceptable drafts. --- ## What's Next? 👉 **[Continue to AI Invoice Processing →](/getting-started/cookbook/ai-invoice-processing)** - Explore autonomous AI capabilities in **[AI Agent](/build/ai-features/ai-agent)**. - Sanitize and format data effortlessly with **[Data Transformation](/build/core-concepts/data-transformation)**. - Learn about branching and fallback routes in **[Switch Step](/build/action-steps/switch)**. --- Source: https://docs.getglow.ai/getting-started/cookbook/ai-invoice-processing # AI Invoice Processing > Have an AI Agent read line items off PDF invoices, put the total in front of a person to approve, then write the rows to your database. **The full picture · about 30 minutes · 4 steps** An AI Agent reads line items off PDF and image invoices, whatever the vendor's layout. The extracted total goes to a person to approve, and only after approval do the line items reach your database. The AI does the reading. The approval and the writing are ordinary steps, so nothing reaches your database that a person has not seen. This is the most involved recipe here, and it brings together everything the earlier ones introduced. Work through one of those first if any piece is unfamiliar. Gmail trigger → [AI Agent](/build/ai-features/ai-agent) → [Human Review](/build/action-steps/user-approval) → Airtable ## What you will use - An **AI Agent**, for reading documents no fixed parser could handle. - A **Human Review** step, which pauses the run until a person decides. - **Run for each item**, to write one database row per line item. ## Prerequisites - A connected **Gmail** or **Google Drive** account (to catch the inbound invoices). - A connected **Notion**, **Airtable**, or **SQL Database** (to store the data). --- ## Building the Workflow ### 1. The Trigger: New Email with Attachment Add a **Gmail** trigger. Set it to listen for new emails in your `billing@acmecorp.com` inbox that contain attachments. ### 2. The AI: Glow Agent (Extraction) Standard parsing steps fail on unpredictable PDFs. Instead, drag an **AI Agent** step onto the canvas. In the agent's **Primary goals**, reference the attachment from the trigger and say what to pull out of it: > _"Read the attached invoice and return strict JSON with this shape: `{ \"vendor_name\": \"string\", \"total\": 0, \"due_date\": \"YYYY-MM-DD\", \"line_items\": [{ \"description\": \"string\", \"price\": 0 }] }`. Use numbers for total and price. Do not add fields that are not present on the invoice."_ Turn on **Parse JSON result** in the same step, so the steps that follow get an object rather than a block of text. Insert the attachment from the Workflow data panel rather than typing a path: the field name comes from your trigger, not from us. The snippets here write the trigger as `1` and the agent as `2`; read the real numbers off your own steps. ### 3. The Safety Net: Human Review You do not want AI blindly paying invoices or writing unverified data to your ERP. Drag a **Human Review** step after the Agent. Configure it to display the vendor name, due date, and formatted total using [Data Transformation](/build/core-concepts/data-transformation): `{{ 2.result.total | format_currency:"$" }}`. The workflow now pauses until someone on your finance team clicks **Approve** in the Glow dashboard. ### 4. The Loop: Process Line Items Once approved, each line item needs to be saved to your database. Add your database action step (e.g., **Airtable: Create Record**). Open that step's repeat badge and switch it to **Each item**, then choose the list of line items the agent extracted: `{{ 2.result.line_items }}`. **Every AI step stores its answer under `result`**, the Agent included, so the path is `2.result.…`. With **Parse JSON result** on you can reach inside it directly, as here. Open the agent's **Executions** tab and read the real shape before you map it: the field names come from the invoice, so they are whatever you asked the agent to return. Map the current item's description and price into the Airtable fields with `{{ item.description }}` and `{{ item.price }}`. **What this costs.** Each item spends one credit, so a 40-line invoice is 40 credits at this step alone. The number of items one step may process is capped by your plan: 50 on Free, 1,000 on Pro, 10,000 on Enterprise. Over the cap, the step is refused rather than quietly processing part of the invoice. For an invoice longer than your cap, split the extracted list into smaller batches before the loop, or move up a plan. > **Why it is built this way.** The AI does one job: reading the invoice (Step > 2). The approval (Step 3), the loop and the database write (Step 4) all run > the same way every time. A misread total is caught by the person approving it, > not by the model. ## Test and go live ### Use a known invoice Send a test invoice with a vendor, total, due date and two line items you can check by eye. Run through the Gmail trigger's test flow and select its attachment from the Workflow data panel when configuring the agent. ### Check the structured result Open the agent's stored output. Confirm `vendor_name`, `total`, `due_date` and `line_items` match the document, and that `line_items` is a list with exactly two records. Stop here if the shape or values differ; later steps depend on both. ### Approve and verify the writes Open the Human Review email while signed in as a member of the workspace. Compare its summary with the source invoice, approve it, then confirm Airtable contains exactly two new rows with the expected descriptions and prices. Reject a second test run and confirm it writes no rows. ### Set the workflow Live Switch the workflow to **Live**, send one controlled invoice from an approved sender and follow it through the review and database. Confirm the final run writes each line once before routing real invoices through it. --- ## What's Next? 👉 **[Build a workflow for your own process →](/build/which-step)** You have completed the cookbook sequence. Start from the job you need to automate, and the guide will point you to the right steps. Go deeper on the concepts this recipe uses: - **[AI Agents](/build/ai-features/ai-agent)**: give an AI Step goals and tools. - **[Human Review](/build/action-steps/user-approval)**: pause a workflow before an important action. --- Source: https://docs.getglow.ai/getting-started/cookbook/github-jira-sync # GitHub & Jira Sync > Automatically create and link Jira tickets when a new GitHub issue is opened, ensuring product and engineering stay aligned. **Starting point · about 15 minutes · 3 steps** Mirror every new GitHub issue into Jira, then comment back on the GitHub issue with the Jira key so each side carries a link to the other. This recipe builds on the earlier mapping work. The final action writes back to the system that started the run, using a value the Jira action produced. GitHub trigger → Jira → Back to GitHub ## What you will use - Two connected apps in one workflow. - A reference to an earlier step's output, to carry the new Jira key back to GitHub. ## Prerequisites - A connected **GitHub** account. - A connected **Jira Cloud** account. --- ## Building the Workflow ### 1. The Trigger: GitHub (Issue Opened) Drag a **GitHub** trigger onto the canvas. Authenticate and select your target repository. Choose the "Issue Opened" event. ### 2. The Action: Jira (Create Issue) Add a **Jira** action step and select "Create Issue". Map the incoming GitHub data into the Jira fields: - **Summary:** `{{ 1.issue.title }}` - **Description:** `Reported by @{{ 1.issue.user.login }} in GitHub: {{ 1.issue.html_url }}`, then press **Enter** for a blank line and add `{{ 1.issue.body }}` - **Issue Type:** Bug (or Task) A trigger's fields sit at the top level, which is why every path here starts `1.` with no wrapper. Insert them with the Workflow data panel rather than typing them. ### 3. The Loop-Back: GitHub (Add Comment) Link the new Jira ticket back to GitHub so the engineer who opened the issue can see it was picked up. Add a second **GitHub** action step and select "Create Issue Comment". The paths below are the shape a Jira **Create Issue** step typically returns. Confirm yours before you rely on them: run step 2 once, open its **Executions** tab, and read the actual field names. What sits after the step number is decided by the step, not by Glow, so there is no wrapper you can assume beyond `ret`. > _"Tracking this internally as `{{ 2.ret.issue.key }}`. You can view the Jira ticket here: `{{ 2.ret.issue.url }}`"_ > **Want true bidirectional sync?** You can build a second workflow in Glow that > does the exact opposite. Trigger on **Jira (Issue Transitioned)**, use a > **Conditions** step to check if it moved to "Done", then use a **GitHub** > action to close the corresponding GitHub issue. ## Test and go live ### Create one unmistakable test issue Open a GitHub issue titled `Glow sync test — safe to close` with a short body. Run the trigger, then inspect the Jira step's stored output before configuring the final comment. Insert the returned issue key and URL from the Workflow data panel; do not rely on the example paths if your connector returns another shape. ### Check both systems Confirm Jira contains one issue with the GitHub title, body and source link. Return to GitHub and confirm one comment links to that same Jira issue. If Jira succeeds but the comment fails, check the repository and issue number mapped into **Create Issue Comment** as well as the Jira output fields. ### Set the workflow Live Close or label the test records according to your team's convention, switch the workflow to **Live**, then create one final controlled GitHub issue. Confirm one Jira issue and one GitHub comment are created. ## What's Next? 👉 **[Continue to Support Ticket Triage with AI →](/getting-started/cookbook/zendesk-sentiment-triage)** - Build the reverse-direction branch described above with the [Conditions Step](/build/action-steps/conditions). - Map fields between the two systems confidently using [Workflow Data](/build/core-concepts/workflow-data). --- Source: https://docs.getglow.ai/getting-started/cookbook/hubspot-slack-alerts # HubSpot to Slack Alerts > Automatically notify your sales team in Slack when a high-value deal is created in HubSpot. **Starting point · about 10 minutes · 3 steps** Post a Slack message when a deal worth over $5,000 is created in HubSpot, and stay quiet about the rest. This is the simplest recipe here: one trigger, one decision, one action. Everything that follows builds on this shape. HubSpot trigger → [Conditions](/build/action-steps/conditions) → Slack ## What you will use - A **Conditions** step, to route on the deal's value. ## Prerequisites - A connected **HubSpot** account. - A connected **Slack** account. --- ## Building the Workflow ### 1. The Trigger: HubSpot (New Deal) Open **Apps** in the dock at the bottom, search for **HubSpot**, and add its trigger. In the App drawer, select the **New Deal** event. The workflow then wakes up the second a deal is created in your CRM. ### 2. The Logic: Condition Step You only want to alert the team when a deal is worth more than $5,000, so the channel does not fill with noise. Drag a **Conditions** step onto the canvas and connect it to your trigger. - **Condition A:** `{{ 1.amount }}` is `Greater Than` `5000`. **Insert variables from the Workflow data panel rather than typing them.** The panel shows the exact output fields from prior steps (e.g. `{{ 1.amount }}`). > If your trigger step is numbered differently on the canvas (e.g. `2`), use > your actual step number instead of `1`. ### 3. The Action: Slack (Send Message) Open **Apps** in the dock, search for **Slack**, and pick **Send Message**. In the step's Account field, choose your connected Slack account, then select the `#sales-alerts` channel. Back in the Conditions step, set **Then go to** on your amount condition to point at the Slack step. Deals under $5,000 match nothing, so they take the **ELSE** route. Leave it empty to end the run there. In the message field, use dynamic variable references and in-field [Data Transformation](/build/core-concepts/data-transformation) to format values cleanly: ```markdown 🚨 _New High-Value Deal Created!_ • _Deal:_ {{ 1.dealname }} • _Amount:_ {{ 1.amount | format_currency:"$" }} • _Owner:_ {{ 1.deal_owner | default:"Unassigned" }} • _HubSpot Link:_ {{ 1.portal_url }} ``` > **Want to build this instantly?** Open the **Workflow Assistant** in your Glow > workspace and enter: *"When a new deal is created in HubSpot, check if the > amount is over $5,000. If true, format the currency and send an alert to the > #sales-alerts Slack channel."* ## Test and go live ### Prove both routes Create one test deal worth `6000` and one worth `4000`. Run each through the trigger. The first should take the named Conditions route and reach Slack; the second should take **ELSE** and finish without a message. ### Check the destination Open `#sales-alerts` and confirm the deal name, formatted amount, owner fallback and link all match the HubSpot record. If a value is blank, reopen the trigger's stored output and insert that field from the Workflow data panel instead of guessing its path. ### Set the workflow Live Switch the workflow from **Draft** to **Live**. Create one final controlled deal above the threshold, then confirm both the successful run in Glow and exactly one new Slack message. --- ## What's Next? 👉 **[Continue to GitHub & Jira Sync →](/getting-started/cookbook/github-jira-sync)** Go deeper on the concepts this recipe uses: - **[Conditions (If/Else)](/build/action-steps/conditions)** - Learn how to build complex branching logic. - **[Data Transformation](/build/core-concepts/data-transformation)** - Format currencies, numbers, and dates directly in tokens. - **[Workflow Data & Mapping](/build/core-concepts/workflow-data)** - Understand how data from the trigger becomes available in downstream steps. --- Source: https://docs.getglow.ai/getting-started/cookbook/lead-enrichment-apollo # B2B Lead Enrichment (Apollo & CRM) > Automatically enrich new inbound leads with Apollo.io data and route them based on company size. **Building on the basics · about 20 minutes · 4 steps** Catch a form submission carrying only a work email, look the company up in Apollo, and route the lead by headcount. Over 1,000 employees goes to an enterprise AE; everything else goes to the automated sequence. Where the earlier recipes reacted to an app's own trigger, this one starts from a webhook. That means it works with any form that can send a web request, not only the ones Glow has a trigger for. [Webhook](/build/triggers/webhook) → Enrichment call → [Conditions](/build/action-steps/conditions) → Your CRM ## What you will use - A **Webhook** trigger, so any form can start the run. - An enrichment call, whose response the routing then reads. - A **Conditions** step, to split the lead by company size. ## Prerequisites - A form that can send a web request to a URL (Typeform, Webflow, or your own site). - A connected **Apollo.io** or **Clearbit** account. - A connected **CRM** (e.g., Salesforce, HubSpot, or Pipedrive). --- ## Building the Workflow ### 1. The Trigger: Form Submission (Webhook) Add a **Webhook** trigger to your canvas. Point your lead form (Typeform, Webflow, or your own site) at this URL, so every submission posts the visitor's email to the workflow. ### 2. The Enrichment: Apollo Add an **Apollo.io** action step, or any other enrichment app you use. Open **Apps** in the dock, pick Apollo.io, and choose its organization enrichment action from the list. Map the domain of the incoming email into the action's domain field. Instead of adding an extra text-splitting step, use [Data Transformation](/build/core-concepts/data-transformation) directly in the token: ``` {{ 1.email | extract_domain }} ``` Apollo returns a large record covering the company's headcount, industry, annual revenue and headquarters location. Run the step once and read its **Executions** tab to see the real field names before you map them. ### 3. The Logic: Condition Step (Routing) Enterprise leads go to a senior AE; everything else goes to the automated sequence. Add a **Conditions** step. - **Enterprise:** `{{ 2.ret.estimated_num_employees }}` is `Greater Than` `1000`. - **Everything else:** leave this to the step's **ELSE** output. You do not write a condition for it. ELSE fires only when none of the conditions above matched, so no lead can take both paths. A second condition testing for _fewer_ than 1,000 employees is not the same thing. Apollo is an app step, so its output sits under `ret`. Insert the path with the Workflow data panel rather than typing it. ### 4. The Action: CRM Update At the end of the Enterprise path, add a **Salesforce** (or HubSpot) step. Select the "Update Lead" action. Map the enriched Apollo data (Industry, Headcount, Revenue) into the corresponding fields in your CRM. Assign the lead owner to your Enterprise AE queue. Off the **ELSE** output, add the step that handles SMB leads: the same CRM update with a different owner, or a step that drops them into a nurture sequence. Leave it unconnected and those leads simply stop there. > **Pro-Tip:** If Apollo cannot find the company, you can add an **Error Path** > to the Apollo step. Route the red error terminal to a **Glow AI Agent** > equipped with a "Search the web" tool to scrape the prospect's company website > as a fallback. ## Test and go live ### Send a controlled webhook Use the Webhook trigger's test flow to send an address at a company domain you are allowed to look up. Keep the payload small, for example `{ "email": "ada@example.com" }`, and replace the address with a domain that produces a known enrichment result in your account. ### Confirm the enrichment shape Open the enrichment step's stored output. Insert its employee-count field into Conditions from the Workflow data panel, then run once with data above your threshold and once with data below it. Confirm the first takes **Enterprise** and the second **ELSE**. ### Check the CRM Open the destination record and confirm industry, headcount, revenue and owner match the enrichment result. If enrichment returns no company, verify the error path or your chosen fallback handles it without creating a misleading CRM record. ### Set the workflow Live Replace the form's test URL with the Live webhook URL, set the workflow **Live**, and submit one controlled form entry. Confirm one run and one correctly routed CRM update. ## What's Next? 👉 **[Continue to AI Support Router with Tools →](/getting-started/cookbook/ai-customer-support-router)** - Build the same routing logic step by step in the [Branching](/getting-started/tutorials/lead-routing-tutorial) tutorial. - Clean and normalize inbound values with [Data Transformation](/build/core-concepts/data-transformation). - Wire up the fallback path described above using [Error Handling & Retries](/build/core-concepts/error-handling). --- Source: https://docs.getglow.ai/getting-started/cookbook/overview # Cookbook & Use-Cases > Worked workflows you can adapt, each explaining why it is built the way it is. Six worked workflows you can copy. Each one explains why it is built the way it is, not only which steps to drag. They are ordered below by how much they ask of you. The first needs a trigger, a decision and an action; the last combines an AI Agent, connected tools, a Human Review step and a loop. Working through them in order means every recipe uses one idea you have already met. | Recipe | Level | Time | What it adds | | ------------------------------------------------------------------------------------ | ---------------------- | ------ | ------------------------------------- | | [HubSpot to Slack Alerts](/getting-started/cookbook/hubspot-slack-alerts) | Starting point | 10 min | Conditions, currency transforms | | [GitHub & Jira Sync](/getting-started/cookbook/github-jira-sync) | Starting point | 15 min | Writing back with an earlier result | | [Support Ticket Triage](/getting-started/cookbook/zendesk-sentiment-triage) | Building on the basics | 20 min | AI Prompt, Switch, HTML cleanup | | [B2B Lead Enrichment](/getting-started/cookbook/lead-enrichment-apollo) | Building on the basics | 20 min | Webhook triggers, domain extraction | | [AI Support Router with Tools](/getting-started/cookbook/ai-customer-support-router) | The full picture | 25 min | AI Agent, CRM tools, token transforms | | [AI Invoice Processing](/getting-started/cookbook/ai-invoice-processing) | The full picture | 30 min | AI Agent, Human Review, loops | New to Glow? [Quickstart: Your First Workflow](/getting-started/tutorials/your-first-workflow) builds a running automation in about five minutes and needs no connected accounts at all. --- ## By team The same six recipes, grouped by who usually wants them. --- ## Sales & CRM Operations - [B2B Lead Enrichment](/getting-started/cookbook/lead-enrichment-apollo): Catch a webhook from a form submission, extract domains with Data Transformation, and use Apollo.io to enrich company data and route leads. - [HubSpot to Slack Alerts](/getting-started/cookbook/hubspot-slack-alerts): Automatically notify your sales team in Slack when a high-value deal is created in HubSpot. Learn how to use **Conditions** to filter out noise. --- ## Customer Support & Operations - [AI Support Router with Tools](/getting-started/cookbook/ai-customer-support-router): Equip an AI Agent with web search and CRM tools to triage customer inquiries, draft grounded responses, and escalate urgent incidents to Slack. - [Support Ticket Triage with AI](/getting-started/cookbook/zendesk-sentiment-triage): Analyze incoming Zendesk tickets with AI, tag them by urgency and sentiment, and escalate angry enterprise customers in Slack. --- ## Engineering & Product - [GitHub & Jira Sync](/getting-started/cookbook/github-jira-sync): Automatically create and link Jira tickets when a new GitHub issue is opened, so product and engineering stay aligned. --- ## Finance & Operations - [AI Invoice Processing](/getting-started/cookbook/ai-invoice-processing): Safely extract line items from unstructured PDF invoices using an AI Agent. Route them to a person for review, then loop through the data to sync with Airtable. ## What's Next? 👉 **[Start with HubSpot to Slack Alerts →](/getting-started/cookbook/hubspot-slack-alerts)** New to workflow building? Complete [Quickstart: Your First Workflow](/getting-started/tutorials/your-first-workflow) before starting the recipes. --- Source: https://docs.getglow.ai/getting-started/cookbook/zendesk-sentiment-triage # Support Ticket Triage with AI > Analyze incoming Zendesk or Intercom tickets with AI, tag them by sentiment, and escalate urgent cases in Slack. **Building on the basics · about 20 minutes · 6 steps** Read every incoming support ticket with AI, decide its sentiment and urgency, and route the angry-and-critical ones straight to a person. The worked steps use Zendesk; you can apply the same pattern to an Intercom new-ticket trigger and its ticket actions. This is the first recipe with AI in it, and the first that routes on an AI's answer rather than a true/false check. Zendesk trigger → [AI Prompt](/build/ai-features/ai-prompt) → [Switch](/build/action-steps/switch) → Zendesk and Slack ## What you will use - An **AI Prompt** step, told to return JSON so the next step can route on it. - A **Switch** step, for routing to one of several branches rather than only true or false. ## Prerequisites - A connected **Zendesk** account with permission to read and update tickets, plus an Escalation Team ready to receive critical tickets. To adapt the recipe, connect Intercom and select its corresponding ticket trigger and actions instead. - A connected **Slack** workspace with the person or channel that should receive escalation alerts. Your company may require an administrator to approve the connection. The AI Prompt step needs nothing connected. It runs on Glow's managed models, with no provider account or API key to set up. --- ## Building the Workflow ### 1. The Trigger: Zendesk (New Ticket) Drag a **Zendesk** trigger onto the canvas. Select the "New Ticket Created" event. This ensures Glow wakes up the second a customer sends an email or submits a form. ### 2. The AI: AI Prompt Step A classification this narrow does not need a full agent. Add an **AI Prompt** step. In the **Instructions** field, tell the model what to decide and in what shape: > _"Analyze the following support ticket. Determine the sentiment (Positive, Neutral, Angry) and the urgency (Low, High, Critical). Return the result as a strict JSON object."_ Reference the sanitized ticket body inside those instructions: `{{ 1.ticket.description | strip_html | trim }}`. Using [Data Transformation](/build/core-concepts/data-transformation) removes raw HTML tags from incoming emails before feeding them to the AI, saving token budget and improving classification accuracy. Switch on **Parse JSON result** in the same step. It is off by default, and without it the answer comes back as a block of JSON _text_ rather than fields you can read. With it on, `{{ 2.result.sentiment }}` and `{{ 2.result.urgency }}` work as written below. Skip it and every ticket takes the Fallback path, because the Switch is reading text rather than the fields it expects. ### 3. The Logic: Switch Step (Escalation) Add a **Switch** step to route the ticket based on the AI's analysis. - **Case 1 (Angry):** `{{ 2.result.sentiment }}` equals `Angry` AND `{{ 2.result.urgency }}` equals `Critical`. - **Fallback:** everything else. This is the step's own fallback output, not a case you write. Switch compares case-sensitively by default, so `Angry` and `angry` are different values. That is why the instructions above name the exact words the model must return. ### 4. The Actions: Tagging & Alerts On the **Fallback** path, add a **Zendesk** action to update the ticket. Put `{{ 2.result.sentiment }}` and `{{ 2.result.urgency }}` in the tags field, so every ticket carries what the AI decided. On the **Angry** path, add two steps: 1. **Zendesk:** Update the ticket priority to `Urgent` and assign it to the Escalation Team. 2. **Slack:** Send an alert to the person or channel you prepared, including the ticket link. ## Test and go live ### Test the escalation route Create a ticket with an unmistakably urgent message, such as `Production login is down for every user and we need help now`. Run it and inspect the AI Prompt result. It should contain the exact `Angry` and `Critical` values the Switch expects before the escalation branch runs. ### Test the fallback route Create a neutral request, such as `Please send me a copy of last month's invoice`. Confirm it takes **Fallback**, receives the classification tags and does not post the urgent Slack alert. ### Check the destinations Open both Zendesk and Slack. Confirm the critical ticket is urgent, assigned to the expected team and represented by one Slack alert. If the Switch cannot read `sentiment` or `urgency`, check that **Parse JSON result** is on and read the AI step's stored output before changing the cases. ### Set the workflow Live Switch the workflow to **Live**, submit one controlled ticket from your test requester and confirm its live execution reaches the expected destination exactly once. ## What's Next? 👉 **[Continue to B2B Lead Enrichment →](/getting-started/cookbook/lead-enrichment-apollo)** - Tune the classification call itself in the [AI Prompt Step](/build/ai-features/ai-prompt). - Clean and prepare incoming text payloads with [Data Transformation](/build/core-concepts/data-transformation). - Add more triage lanes than angry and default with the [Switch Step](/build/action-steps/switch). --- Source: https://docs.getglow.ai/getting-started/learning-path # Learning Path > A compact route from your first Glow workflow to testing and operating it with confidence. Use this optional curriculum when you want one route through the documentation. If you prefer to learn by solving a specific problem, start with [Choose the Right Step](/build/which-step) or a [cookbook recipe](/getting-started/cookbook/overview). ### Build and run your first workflow Follow [Quickstart: Your First Workflow](/getting-started/tutorials/your-first-workflow). In about five minutes, you will place a trigger and an AI Step, run the workflow and inspect the result. **After this you can** build a short workflow and read what a Step returned. ### Connect an app and map real data Follow [Build It Yourself](/getting-started/tutorials/tutorial-basics), then use [Mapping or Transforming Data](/build/core-concepts/mapping-or-transforming) when the destination needs a different value or shape. **After this you can** connect an account, inspect test data and pass earlier output into a later Step. ### Add routing Follow [Branching](/getting-started/tutorials/lead-routing-tutorial). You will create alternative paths, configure exact rules and test where the data goes. **After this you can** choose a route and bring alternative branches back into the workflow. ### Build a workflow for your own outcome Choose the [cookbook recipe](/getting-started/cookbook/overview) closest to your work, or use [Choose the Right Step](/build/which-step) to assemble a different pattern. **After this you can** adapt a complete workflow shape to a process in your organization. ### Test it, switch it to Live and watch it Read [Operating Workflows](/build/core-concepts/operating-workflows) before relying on the result. It covers controlled testing, checking the destination, switching from Draft to Live and monitoring the first automatic runs. **After this you can** move from a successful manual run to a Live workflow and verify its real behavior. ## Optional: build with someone else [Working Together](/getting-started/tutorials/collaborating-on-a-canvas) shows how to share a workflow and edit the same canvas in real time. Nothing else in the learning path depends on completing it. ## What's Next? - 👉 **[Build Your First Workflow →](/getting-started/tutorials/your-first-workflow)** - Building for another company? Start with [Working With Clients](/msp/overview). - Already know the outcome? Use [Choose the Right Step](/build/which-step). --- Source: https://docs.getglow.ai/getting-started/migrating-to-glow # Migrating to Glow > Plan, rebuild, test and cut over an automation from another platform without carrying across the wrong assumptions. Use this checklist to move an existing automation into Glow, validate its behavior and switch it over safely. Import is available for n8n and Make; migrations from other platforms start with the same plan but use a manual rebuild. ## Migration Checklist ### 1. Record the current behavior Before changing tools, capture what the automation must preserve: - how it starts, including schedules, webhook callers and app triggers; - the app accounts and permissions it uses; - its branches, loops, delays and failure paths; - custom code or platform-specific steps; - representative inputs, expected outputs and external side effects. This becomes the acceptance checklist for the Glow version. Focus on behavior rather than reproducing the old canvas step for step. ### 2. Choose the migration route - **From n8n or Make:** follow [Importing a Workflow](/getting-started/templates/import-and-export) for the supported file format, import procedure and what requires attention afterward. - **From another platform:** rebuild from the outcome backward. [Choose the Right Step](/build/which-step) maps common workflow shapes and goals to Glow Steps. Treat an imported workflow as a starting point. Review it with the same care as a manual rebuild. ### 3. Complete the Glow workflow - Reconnect every app account used by the workflow. - Open each Step and confirm its operation, fields and mapped data. - Replace custom code and platform-specific actions with the appropriate Glow Steps. - Rebuild routing and repeated work using the patterns in [Choose the Right Step](/build/which-step). - Review retry and failure settings for Steps that call external services. ### 4. Test against known cases Run representative inputs through the Glow workflow and compare the results with your acceptance checklist. Include every route, empty or missing values, lists with more than one item and a controlled failure. Use test destinations where a run sends messages, creates records or changes external data. Inspect the output of each Step before testing the workflow end to end. ### 5. Cut over 1. Keep the original automation available while you validate the Glow version. 2. Switch the Glow workflow to **Live** only when its trigger, connections and outputs are ready. 3. Confirm a real run completes as expected. 4. Disable the original automation so both platforms cannot process the same event. 5. Review the first live runs and keep the previous configuration available until the migration is accepted. ## Platform Differences | Area | What changes in Glow | What to do during migration | | ---------------------- | ----------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Terminology | Familiar concepts may use different names, including **Step**, **Workflow Assistant** and **App drawer**. | Use the [Glossary](/reference/glossary) instead of translating terms from memory. | | Workflow shape | Routing and repeated work may need a different canvas structure rather than a one-to-one Step replacement. | Start with the patterns and decision tables in [Choose the Right Step](/build/which-step). | | Data mapping | Fields can include data selected from earlier Steps alongside typed text. | Reinsert each dynamic value and inspect the producing Step's output rather than copying expressions from the old platform. See [Workflow Data](/build/core-concepts/workflow-data). | | Testing and activation | You can test while the workflow is in **Draft**. Switching it to **Live** activates its configured trigger. | Test with controlled inputs and destinations before the cutover. See [Operating Workflows](/build/core-concepts/operating-workflows). | | Failure handling | Retry and failure behavior is configured on the Step. | Review these settings explicitly instead of assuming the previous platform's policy carries over. See [Error Handling & Retries](/build/core-concepts/error-handling). | ## What's Next? - 👉 **[Import Your Workflow →](/getting-started/templates/import-and-export)** - Rebuilding from another platform? Start with [Choose the Right Step](/build/which-step). - Need to translate a product term? Keep the [Glossary](/reference/glossary) open. --- Source: https://docs.getglow.ai/getting-started/navigating-the-dashboard # Navigating the Dashboard > Where everything lives in Glow: the workspace tabs, the workspace menu, and how to get from one to the other. Signing in puts you in a workspace, on its dashboard. Everything else is one click away in the sidebar down the left. On a brand-new workspace the dashboard opens with a box to describe the workflow you want, alongside templates to browse and a short tour. Once your automations have run a few times, that gives way to the panel below: what ran, what needs attention, and what is live.  *The dashboard. The sidebar holds everything in the workspace; the panel shows what has been running.* ## The sidebar The sidebar down the left holds everything in the workspace. Your workspace name sits at the top; clicking it switches to another.  *The five main entries sit at the top, with Team Assets, Support and Community grouped below them.* | Item | What it holds | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Dashboard** | Runs over the last few days, how many workflows are live, and what ran recently. | | **Automations** | Your workflows, as cards or a list. Each shows its run count and whether it is Draft or Live. At the top, **"What would you like to automate today?"** takes a plain-language sentence and drafts the workflow for you, often the fastest way to start. | | **Marketplace** | The public [template gallery](/getting-started/templates/sharing-and-templates). | | **Activity** | Alerts needing attention and every run in the workspace, with a count on the item when alerts are waiting. See [Activity](/manage/workspace-settings/activity). | | **Connections** | The apps this workspace has connected, and the full library you can connect. | Below those, three groups that fold away: | Group | What is in it | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Team Assets** | **[Deployment](/getting-started/templates/deployment)**, every workflow you have published and where it is live; **Files** your workflows read at run time; and **Variables**: team variables and secrets, sharing one table. See [Secrets and Variables](/manage/workspace-settings/secrets-and-variables). | | **Support** | This documentation, video tutorials, and a way to send feedback. | | **Community** | The forum and the Discord server. | ## The header Two things live along the top: - **Open workflows**: tabs for the workflows you have open, so you can move between them without going back to the list. - **Your name**, top right, which opens your [account settings](/manage/workspace-settings/personal-settings). ## The workspace menu Clicking your workspace name at the top of the sidebar opens a short menu. | Item | What it does | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | **Manage team** | Invite people, set roles, and see who is in the workspace. See [Team Management](/manage/workspace-settings/team-management). | | **Billing & Usage** | Usage and pricing for every workspace role; financial details and billing management for Admins. See [Billing & Usage](/manage/billing/overview). | | **Other teams** | Every workspace you belong to, and **Create new** to start another. | ## Finding your way around | Task | Where to go | | --------------------------- | ------------------------------------------------------------------ | | Create a workflow | **Workflow** on the dashboard, or **Create new** under Automations | | Import from n8n or Make | **Import JSON** under Automations | | Group workflows together | **Create new → New Folder** under Automations | | Find a deleted workflow | **Filter → Archive** under Automations | | Connect an app | **Connections** | | Store an API key | **Team Assets → Variables** | | Upload a file a flow uses | **Team Assets → Files** | | See what you have published | **Team Assets → Deployment** | | See what needs attention | **Activity** | | Export a list of runs | **Activity** → Download CSV | | Invite a teammate | **Invite** on the dashboard, or workspace menu → **Manage team** | | Check credit usage | Workspace menu → **Billing & Usage → Usage** | | Switch workspace | The workspace name at the top of the sidebar | | Browse templates | **Marketplace** | | Change your password | Your name, top right | ## Keeping the list manageable ### Folders Group workflows under **Automations**: 1. Click **Create new → New Folder**, choose a name, and select a color. 2. Drag workflow cards onto the folder to move them in. 3. Open any folder and create nested folders as needed. _Note: Folders organize your view; they do not change workspace permissions._ ### Archive **Deleting a workflow moves it to Archive** with a **30-day** recovery window. - Restoring brings the workflow back **switched off** to prevent unintended executions. - Deleting directly from Archive is permanent. **Deleting a workflow moves it to Archive** rather than removing it, and you can restore it from there for **30 days**. Each card shows the date it will be removed on. A restored workflow comes back **switched off**, so it does not start running the moment it returns. Deleting from Archive yourself is immediate and permanent. Archive is a way back from a mistake, not a place to keep something — to hold a workflow indefinitely without it running, leave it in Automations switched off. ## What's Next? - Set up who can do what in [Setting Up Your Team](/getting-started/setting-up-your-team). - Build something with the [step-by-step tutorials](/getting-started/tutorials/your-first-workflow). --- Source: https://docs.getglow.ai/getting-started/setting-up-your-team # Setting Up Your Team > Create a team, invite people to it, and choose what each role can do. A team is a shared workspace. Your workflows, app connections, secrets and files all belong to one, even when you work alone. **You already have one.** Signing up walks you through **Set Up Your Workspace**, so the team you are working in now was created with your account. This page is about the rest: adding more teams, inviting people, and deciding what each of them can do. ## Create another team One team is enough for most companies. A second is worth having when the work should not mix: a workspace per client if you build for other companies, a separate one for a side project, or one for experiments you do not want beside production automations. 1. Click your workspace name at the top of the sidebar and choose **Create new** under **Other teams**. 2. Enter a **Team Name**. This will appear in your sidebar and URLs. 3. Optionally add a description so members know the team's purpose. 4. Click **Create Team** to finish. Switching between teams switches the whole view: each one has its own workflows, connections, variables and run history, and nothing is shared between them. > **Your role is on your account, not on a team.** The Individual / Team, > Consultant or MSP answer you gave when you signed up describes how you work, > so it carries across every team you belong to and is not asked again here. It > is not a permission level either: what you and your teammates may do is set by > [Team Roles](#team-roles) below. > > **Building for other companies?** Consultant and MSP accounts get the > multi-tenancy platform: a workspace per client, delegation grants your client > consents to, and one console across every client you work with. Your account team > turns it on with you. See [Working With Clients](/msp/overview). You are automatically assigned the **Admin** role for any team you create. > **Tip:** You can create multiple teams to separate projects or departments. > For example, you might have one team for marketing automations and another for > engineering ops. ## Invite team members Once your team is created, you can invite collaborators. 1. Open the team and click **Invite Members** to open the invite modal. 2. Enter one or more email addresses. 3. Select a role for each invitee (see roles below). 4. Click **Send Invites**. Invitees receive an email with an invite code. They can accept the invitation by entering the code when prompted, which adds them to the team immediately. ## Team roles Glow provides three roles. They control **team administration**: who can bill, who can manage people, who can change shared values. | Role | Can | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Admin** | Everything below, plus billing management, invoices and payments, team settings, inviting and removing anyone, changing any role, and deleting the team. | | **Manager** | Everything below, plus inviting Managers and Members, changing Manager and Member roles, and managing team variables. | | **Member** | Build, edit and run workflows. View usage and pricing. Manage team files. View team variables. Invite further Members, and cancel or remove invitations they sent. |  *The role picker shown when you invite someone to the team.* > **Roles govern team administration, not workflow access.** Every role, Member > included, can create, edit and run any workflow in the team. To limit who can > open a particular workflow, set that on the workflow itself. See [Workflow > Visibility and Sharing](/build/core-concepts/workflow-visibility). Assigning > someone Member will not give you read-only access. Two things worth knowing before you assign roles: - **Members can invite other Members.** Bringing one person in lets them bring in others at the same level. - **Team files are open to everyone.** Any role can upload, rename and delete them, so a shared configuration file can be removed by anyone on the team. Secrets and team variables are the ones genuinely restricted. Members can see variable keys but not change them, and only Admins and Managers manage them. Choose roles based on each person's responsibility. You can change a member's role at any time from the team settings. ## Removing someone Admins and Managers remove people from team settings. Removing someone takes away their access to the workspace; it does not delete the workflows they built or the connections they authorised, since those belong to the team rather than to them. Worth checking before someone leaves: any connection they authorised under a personal account of their own. The connection stays, but it points at an account the team no longer controls, and the steps using it fail once that account is closed. Re-authorise those under an account the team owns. ## What teams contain Each team workspace holds four types of resources: - **Workflows**: the automations you build on the canvas - **Connections**: authenticated links to third-party apps (e.g., Slack, Google Sheets, Stripe) - **Secrets**: API keys, tokens, and other sensitive values used by your workflows - **Files**: uploaded assets that workflows can reference or process All of these resources are shared across the team according to each member's role permissions. ## What's Next? - 👉 **[Navigate the Dashboard →](/getting-started/navigating-the-dashboard)** to find workflows, settings and the control for creating an automation. - Ready to build already? Head to [Your First Workflow](/getting-started/tutorials/your-first-workflow). --- Source: https://docs.getglow.ai/getting-started/templates/deployment # The Deployment Page > Manage every workflow you have published from one table: whether it runs as a step or a form, and who it is deployed to. **Deployment** is your workspace's central command center for packaging, versioning, and distributing automations. From a single unified ledger, you choose whether a workflow runs as an editable **Step template** or a standalone **Form interface**, create semantic snapshots, and deploy versions to the public Marketplace or managed client workspaces. Navigate to **Team Assets → Deployment** in the left sidebar to manage all published automations across your workspace. > **Deploying to Clients?** If you manage multi-tenant clients as a consultancy > or MSP, [Choose a Deployment Method](/msp/choosing-a-deployment-method) > compares this versioned deployment route with the one-client direct Admin Push > flow. --- ## Understanding the Deployment Table The Deployment table lists every publishable workflow in your workspace alongside its active interface type and distribution status: | Column | What it tells you | Operational Actions | | :-------------- | :-------------------------------------------------- | :------------------------------------------------------------------------- | | **Interface** | How the workflow is consumed: **Step** or **Form**. | Click the dropdown to switch interfaces or configure form fields. | | **Automations** | The workflow name and internal identifier. | Click the workflow title to open the visual canvas. | | **Author** | The workspace member who authored the automation. | Identifies team ownership and maintainers. | | **Last edited** | Relative timestamp of the most recent change. | `2h ago`, `yesterday`, `3d ago`. | | **Deployed to** | Where the workflow is actively published. | Shows **Deploy** (unpublished), Marketplace badge, or client tenant logos. | > **Audit Unpublished Workflows:** Read down the **Deployed to** column to > identify completed automations displaying a blue **Deploy** link. These are > workflows that have been built but not yet distributed to users or clients. --- ## Selecting an Interface: Step vs. Form The **Interface** control defines how end-users interact with your workflow: - [Step Interface (Default)](/getting-started/templates/step-templates): The workflow is distributed as a modular building block. Users fork the full canvas to customize logic or call it as a child Subflow. - [Form Interface](/getting-started/templates/form-templates): The workflow is packaged as a standalone web form. Users input parameters and click Run without seeing canvas steps or credentials. ### Switch Interface Mode Click the **Interface** chip in the table row and select **Form** or **Step**. ### Configure Form Fields (Form Mode) If selecting **Form** on a workflow without configured inputs, click the yellow **Settings (⚙️)** icon. The form builder drawer opens beside the table, allowing you to select canvas fields, set custom labels, and define validation rules without leaving the Deployment page. --- ## The Versioned Deployment Flow Clicking **Deploy** on any workflow row (or selecting **Share → Deploy** on the visual canvas) opens the two-step deployment modal: ### 1. Basic Info & Interface Selection Verify the workflow title and category. Click **✦ Generate descriptions** to have Glow analyze your canvas steps and automatically draft a summary using your Glow AI token allowance. Confirm whether the workflow deploys as a **Step** or **Form** interface. ### 2. Version & Target Destination Select an existing version snapshot or click **+ New version** to create an immutable version tag (e.g. `v1.2.0`) with release notes. Choose your destination: publish publicly to the **Marketplace** or push independent draft copies to up to **50 client workspaces**. --- ## Credential Isolation During Deployment Glow's multi-tenant architecture strictly enforces zero-trust credential boundaries during all deployment operations: > **Zero-Trust Credential Isolation:** - **Zero credential leakage:** Host > templates never export third-party API tokens, passwords, or OAuth secrets. - > **Client-owned execution:** Target client workspaces receive independent > unconfigured steps that execute using the client's own credentials. - **Safe > account rebinding:** Automatic connection binding happens only when exactly > one healthy matching account exists in the target workspace. - > **Pre-activation review:** Workflows arrive in **Draft** mode so client Admins > can review connections before going Live. - **No Credential Leakage:** API keys, OAuth tokens, and workspace secrets configured in your agency workspace are stripped automatically during packaging. - **Client Account Binding:** When a client workspace activates a deployed workflow, steps automatically bind to existing client connections if exactly one healthy account exists for that service; otherwise, the client Admin selects the appropriate connected account. ## What's Next? - 👉 **[Form Templates →](/getting-started/templates/form-templates)**: Customize input fields, validation, and pre-filled default values. - **[Step Templates](/getting-started/templates/step-templates)**: Publish editable canvas blueprints to the Marketplace. - **[Choose a Deployment Method](/msp/choosing-a-deployment-method)**: Compare workspace version deployment with direct MSP Admin Push. --- Source: https://docs.getglow.ai/getting-started/templates/form-templates # Form Templates > Turn complex workflows into simple, executable interfaces for non-technical users. A **Form Template** turns a visual workflow into an interactive, standalone form. Non-technical colleagues, clients, or external partners simply fill in the defined input fields and click **Run**. The runner never sees the underlying canvas, intermediate logic steps, or sensitive connected credentials. Use a Form Template whenever an automation should be operated by people who need the result without having to learn or modify the workflow. - [Form Interface](#how-the-form-interface-works): Single-submission web forms for structured intake (lead forms, onboarding, invoice processing). - [Chat Interface](/build/triggers/chat): Multi-turn conversational interfaces for AI assistants, customer support bots, and Slack helpers. - [Step Templates](/getting-started/templates/step-templates): Full editable canvas templates for builders to fork, customize, and extend in their own workspace. --- ## Comparing Deployment Interfaces | Feature | Form Template (Form Interface) | Chat Interface (Chat Trigger) | Step Template (Canvas Fork) | | :-------------------- | :------------------------------------------ | :--------------------------------------------- | :---------------------------------------- | | **User Interaction** | Structured web form submission | Interactive multi-turn chat | Full visual canvas editor | | **Best For** | Data intake, HR requests, report generation | Support bots, documentation Q&A, Slack helpers | Reusable workflow patterns & blueprints | | **Canvas Visibility** | Completely hidden | Completely hidden | Full canvas visible & editable | | **Credentials** | Uses builder's secure workspace connections | Uses builder's secure workspace connections | Requires forker to connect own accounts | | **Entry Point** | Form link or workspace Deployment | Hosted chat page or Slack bot | Marketplace or workspace template library | --- ## How to Build a Form Interface You build the automation on the multiplayer canvas as normal, then choose which fields across your steps should be exposed on the public form: ### Open the Form Interface Drawer On your workflow canvas, click **More (⋮)** in the top-right toolbar and select **Form interface** (or click the **Interfaces** icon in the bottom-right dock). ### Select Fields from Canvas Steps Click **Add fields**. The drawer displays all steps on your canvas. Click any step to inspect its configurable parameters, then check the fields you want the form runner to provide. ### Configure Labels, Descriptions & Validation For each exposed field, customize the presentation: - **Field Label:** Give the field a clear, non-technical title (e.g. `Customer Email Address` instead of `cust_email`). - **Placeholder & Helper Note:** Add instructions or format hints (e.g. `e.g. jane@company.com`). - **Required toggle:** Mark whether the field is mandatory before the runner can submit the form. ### Enable Pre-filled Default Values (Optional) Toggle on **Default values** to populate the form with initial values derived from your current canvas step configuration. This lets runners submit standard requests with a single click while still allowing customization when needed. ### Preview and Test Test the form directly inside the drawer preview. Enter sample inputs and confirm that values map correctly into your canvas steps before deploying. --- ## Supported Form Field Types Glow automatically renders the appropriate UI control based on the underlying step parameter type: | Field Type | Form Control | Examples & Use Cases | | :----------------------- | :----------------------------------- | :----------------------------------------------------------- | | **Short Text** | Single-line text input | Customer names, email addresses, order IDs | | **Long Text / Markdown** | Multi-line textarea | Support ticket descriptions, feedback comments | | **Numeric** | Number input with increment controls | Invoice amounts, item quantities, threshold caps | | **Single Select** | Dropdown selection menu | Priority tiers (`Low`, `Medium`, `High`), department choices | | **Date & Time** | Calendar date/time picker | Appointment dates, report start/end dates | | **File Upload** | Drag-and-drop file uploader | PDF invoices, CSV datasets, image receipts | --- ## Deploying and Sharing the Form Once your form interface is configured, you have several ways to distribute it: **Generate a standalone hosted web URL:** 1. Switch your workflow from **Draft** to **Live** in the top navigation bar. 2. In the canvas header, click **Share → Form Link**. 3. Choose access permissions: - **Public:** Anyone holding the link can submit the form. - **Workspace Members Only:** Requires signing in with an authenticated workspace account. **Distribute across client workspaces:** 1. Open **Team Assets → Deployment** in the left sidebar. 2. Locate your workflow row and confirm the **Interface** column is set to **Form**. 3. Click **Deploy** to snapshot a version and push independent copies to up to 50 client workspaces. 4. See [The Deployment Page](/getting-started/templates/deployment) for full versioning details. **Share with the global Glow community:** 1. Open **Workflow Settings → Templates**. 2. Click **Publish to Marketplace**. 3. Add a descriptive overview, select domain tags, and submit your version. --- ## Runner Experience and Result Delivery When an end-user opens your Form Template link: 1. **Clean Branded View:** They see a clean, distraction-free interface displaying only your configured input fields, descriptions, and a prominent **Run** button. 2. **Real-Time Validation:** Required fields and format constraints (e.g. valid email syntax or file upload size limits) are validated client-side before submission. 3. **Execution Delivery:** - **Synchronous Workflows:** If the workflow produces an immediate output (such as a generated summary or calculated score), the result displays directly on the completion screen. - **Asynchronous Workflows:** For background automations (such as updating Salesforce records or sending Slack notifications), the runner receives a confirmation receipt while work completes in the background. --- ## Real-World Use Case Patterns | Template Pattern | Canvas Workflow | Runner Experience | | :------------------------------- | :---------------------------------------------------------------------------------- | :---------------------------------------------------------------------------------------- | | **Client Onboarding Intake** | Form Intake → Create HubSpot Contact → Generate Google Drive Folder → Alert Slack | Client account manager enters customer details and uploads contract PDF. | | **AI Expense Receipt Processor** | Form Intake (File Upload) → AI Prompt (Extract JSON) → Validate → Quickbooks Write | Employee uploads photo of receipt; total amount and category are extracted and submitted. | | **Custom Data Query Generator** | Form Intake (Date Range & Filters) → Database Query → Format CSV → Email Attachment | Operations team selects date range and receives automated CSV export in their inbox. | ## What's Next? - 👉 **[The Deployment Page →](/getting-started/templates/deployment)**: Manage published versions and deploy forms across client workspaces. - **[Chat Trigger & Interfaces](/build/triggers/chat)**: Create multi-turn conversational AI interfaces and Slack bots. - **[Step Templates](/getting-started/templates/step-templates)**: Share editable canvas blueprints with workflow builders. --- Source: https://docs.getglow.ai/getting-started/templates/import-and-export # Importing a Workflow > Bring a workflow into Glow from n8n or Make by importing its exported JSON. Glow imports workflows exported from other automation platforms, so moving an existing automation across does not mean rebuilding it step by step. **Supported sources: n8n and Make.** ## How to import 1. Export the workflow from your current tool as JSON. From n8n, that is the workflow's JSON. From Make, it is the scenario blueprint. 2. In Glow, open your workflow list and choose **Import JSON**. 3. Upload the file. The dialog carries instructions for exporting from each supported platform, so you do not need to look them up separately. Glow builds the canvas from the file: the steps, how they connect, and the configuration it can map. Anything without a direct equivalent in Glow is left for you to complete. That is normal when moving between platforms that model things differently. > **Credentials never travel in an exported file**, from any platform. Every > connection the workflow uses has to be authorised again in Glow before it will > run. See [Connecting an App](/manage/apps-and-integrations/connecting-an-app). ## Sharing a workflow inside Glow Inside Glow, a workflow moves as a template or a saved version rather than as a file. Use the route built for the job: - **[Templates](/getting-started/templates/sharing-and-templates)**: publish a workflow so others can fork their own copy. This is how to share a pattern with your team or the wider community. - **[Version history](/build/core-concepts/versioning)**: save a version before a risky change and restore it if the change goes wrong. This is what to reach for if you were thinking of exporting as a backup. ## What's Next? - Plan a move from another platform with [Migrating to Glow](/getting-started/migrating-to-glow). - Reconnect the credentials an import leaves empty in [Connecting an App](/manage/apps-and-integrations/connecting-an-app). --- Source: https://docs.getglow.ai/getting-started/templates/overview # Templates and Reuse > Understand the difference between Marketplace Step Templates, Form Interfaces, and Workspace Deployments in Glow. Glow provides distinct mechanisms for reusing, sharing, and executing automations depending on who will operate the workflow: - [1. Step Templates (Marketplace)](/getting-started/templates/step-templates): Pre-built workflow blueprints from the Marketplace. Fork the full editable canvas into your workspace and customize steps. - [2. Form Interfaces](/getting-started/templates/form-templates): Standalone form execution wrappers. End-users fill in input fields and run workflows without touching the canvas or credentials. - [3. The Deployment Hub](/getting-started/templates/deployment): Centralized table to manage workflow interfaces, snapshot semantic versions, and push updates to client workspaces. --- ## Templates vs. Interfaces: Key Distinctions It is important to understand the fundamental difference between **Marketplace Templates** and **Workflow Interfaces**: | Concept | What It Is | Who Uses It | Canvas & Credentials | | :-------------------------------------------------------- | :-------------------------------------------------------------------- | :------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------- | | **Step Template** (Marketplace) | A cloneable workflow blueprint in the public or team library. | **Workflow Builders:** Users who want to fork, edit, and maintain the automation. | Full canvas copied; forker connects their own app credentials. | | **Form Interface** (Canvas / Deployment) | A front-end form runner built on top of an active workspace workflow. | **End-Users / Clients:** People who simply submit requests or data to trigger the run. | Canvas completely hidden; securely uses the author's workspace connections. | | **Chat Interface** ([Chat Trigger](/build/triggers/chat)) | A conversational assistant interface (hosted web page or Slack bot). | **Chat Visitors & Teammates:** People who converse with the workflow interactively. | Canvas hidden; uses workspace connections and returns AI responses. | > **Zero Credential Sharing:** When publishing a template to the Marketplace or > deploying to a client workspace, third-party API keys and OAuth credentials > are never exported. Deployed workflows run securely using the destination > workspace's authorized connections. --- ## The Public Marketplace Explore pre-built workflows designed by the Glow community and certified partners: > **Public Template Gallery:** > [https://app.getglow.ai/templates/](https://app.getglow.ai/templates/)  *A template's page previews its steps, category, required app connections, and interface type before you fork it.* ## What's Next? - 👉 **[Step Templates →](/getting-started/templates/step-templates)**: Browse, fork, and publish editable canvas blueprints. - **[Form Templates](/getting-started/templates/form-templates)**: Turn any workflow into an interactive form runner for non-technical users. - **[The Deployment Page](/getting-started/templates/deployment)**: Manage semantic versions and distribute workflows across client workspaces. - **[Importing & Exporting Workflows](/getting-started/templates/import-and-export)**: Import existing automations from n8n or Make. --- Source: https://docs.getglow.ai/getting-started/templates/sharing-and-templates # Publishing and Forking > Publish a workflow as a reusable template on the Marketplace, and keep the published copy in step with your edits. Glow lets you publish workflows as reusable templates that anyone can fork and adapt. Publish one to the Marketplace and the wider Glow community can find it. A workflow you created from a blank canvas can be published. A workflow forked from another template cannot be republished; start a new workflow if you intend to share an adapted version. ## Publishing a workflow as a template To publish a workflow as a template, use either the direct Marketplace route below or open the broader modal from **Share → Deploy**. Both publish a selected workflow version to the same Marketplace; the modal also exposes client deployment in an MSP workspace. 1. Open the workflow you want to share. 2. Open **Workflow Settings** and find the version section. 3. Choose **Publish to Marketplace**. 4. Fill in the details others will browse by: a title, a description of what it does and when to use it, who it is for, how it works, what setting it up takes, and what it needs. **Generate descriptions** drafts all of these from your canvas and suggests categories, so you edit rather than start from blank. Once published, the template appears in the gallery for others to use. **Generate descriptions reads the canvas in front of you**, including edits you have not saved, so a workflow you have just reshaped is described as it is now rather than as it was. It reads the steps, their names and how they connect — never the values you configured them with. Changing the workflow afterwards does not update what you published. The same button now reads **Publish new version**. Press it when you want the marketplace copy to catch up with the workflow. > **Tip:** Read the generated description as somebody who has never seen the > workflow. It knows what the steps are, not why you built them — the problem it > solves and what the person running it needs to know are yours to add. ## Forking a template  *The template gallery. Search or filter by category, then use Use template to fork a copy into your own workspace.* When you find a template you want to use:  *A template's detail page previews the actual workflow before you fork it, and lists the connections it will need under Requirements.* 1. Open the template detail page. 2. Click **Use template**. 3. Select the team where you want to create the new workflow. 4. Glow creates a full copy of the workflow in your team. You can edit it freely, and your changes do not affect the original template. Forking is non-destructive. The original template and your copy are completely independent after forking. > A workflow you forked from a template cannot itself be published as a > template. If you want to share an adapted version, build it in a workflow that > did not start as a fork. ## When the original template is updated If the template you forked from is published again with changes, your workflow shows a banner saying a new version is available. **Show details** compares the two so you can see what changed. You apply the changes yourself: read the comparison and make the ones you want by hand. Your copy is never modified without you doing it, so a template that changes underneath you cannot break a workflow already running. ## Creator profiles Your name and picture appear on every template you publish, and each card links through to a creator page. The name and picture on your [account settings](/manage/workspace-settings/personal-settings) are what a reader sees attributed to your work, so those are the pair worth filling in. ## Best practices for templates - **Keep templates self-contained.** Avoid referencing team-specific secrets or files that a new user would not have. If your workflow needs credentials, document that clearly in the description. - **Use descriptive step names.** When someone forks your template, clear naming helps them understand the workflow without needing to ask you. - **Version your templates.** If you significantly update a workflow, consider publishing a new version of the template rather than overwriting the old one. ## What's Next? - Keep the logic hidden and give end-users a simple interface with [Form Templates](/getting-started/templates/form-templates). - Track template revisions properly using [Versioning](/build/core-concepts/versioning). - See everything you have published in one table on [The Deployment Page](/getting-started/templates/deployment). --- Source: https://docs.getglow.ai/getting-started/templates/step-templates # Step Templates > Browse, fork, and publish classic workflow blueprints to share working automations across your team. A Step Template shares a workflow as a full, editable copy. Using one clones the whole canvas into your own workspace: triggers, steps, connections, and logic. This gives you full freedom to dissect how the automation was built and customize it to fit your needs. ## Choosing the Right Template Type Two settings decide how a workflow reaches other people, and they live in different places. **Form interface**, under **More (⋮)** in the canvas controls, sets the shape: leave it alone and the workflow is a **Step template** others fork onto their own canvas, or build a form and it becomes a **Form template** they run by filling it in. **Workflow Settings** is where you actually publish it. See [Publishing and Forking](/getting-started/templates/sharing-and-templates). - **Use a Step template when:** whoever you are sharing with will maintain the workflow. They get the canvas and can change how it works. - **Use a Form template when:** whoever runs the automation is not the one maintaining it. They see a form, fill it in, and click Run. They never open the canvas. The **Marketplace** in the sidebar is your catalog of pre-built workflows. Each template includes a name, description, category, and an overview of the apps it uses. That lets you scan quickly for what you need.  *Each card previews the apps a template connects, so you can judge at a glance what credentials you'll need.* You can filter templates by category (e.g., Sales, Marketing, Engineering) and search by keyword. ## Forking a Template When you find a template that fits your use case, click **Use template** to create a copy in your own workspace. Forking gives you a fully editable version of the workflow. The original template is not affected by any of your changes. You own this new copy entirely. > **Tip:** After forking a template, review each step's configuration before > hitting Run. Templates are built for general use, so you will need to > authenticate your own app connections, signing in to your Slack or Gmail > account for example. Check that the data mapping matches your environment too. ## Publishing your workflows as templates A workflow you created from a blank canvas can be published as a Step Template. A workflow forked from another template cannot be republished; start a new workflow if you intend to share an adapted version. When you publish a workflow, you fill in the details others browse by: - **Name**: A clear, descriptive title. - **Description**: A summary of what the workflow does and the problem it solves. - **Category**: The business domain (e.g., Operations, Support). - **Icon**: A visual identifier for the gallery. > **Check what your fields hold before you publish.** Glow removes two things > for you: credential fields, and the account each step is connected to. Whoever > forks the template reconnects their own accounts. > > Everything else you typed travels with the template. A key pasted into an > ordinary text field, an internal hostname, a real customer's name in a sample > message. All of it is copied verbatim into a stranger's workspace. Read > through your steps and clear anything you would not publish deliberately. ## Best practices for builders If you are publishing a template for your team or the community, follow these guidelines to ensure it's easy to adopt: - **Use descriptive step names:** Rename generic steps like `HTTP Request 1` to `Fetch User Data`. This helps the person forking the template understand your logic instantly. - **Leave Sticky Notes:** Since Glow has an infinite canvas, drop sticky notes next to complex branching logic to explain _why_ you built it that way. - **Test End-to-End:** Before publishing, run the workflow with test data to ensure there are no dead ends or broken references. A template that works out of the box earns trust. ## What's Next? - Give non-technical users a form instead of the canvas with [Form Templates](/getting-started/templates/form-templates). - Walk through the publishing details in [Publishing and Forking](/getting-started/templates/sharing-and-templates). --- Source: https://docs.getglow.ai/getting-started/tutorials/collaborating-on-a-canvas # Working Together > Invite a colleague onto the canvas and edit the same workflow at the same time. This optional tutorial shows how two people can edit the same workflow at once, see each other's cursors and divide the work without creating separate copies. It continues from the workflow built in Tutorial 3, but nothing else in the learning sequence depends on completing it. > **What you will learn:** Sharing a workflow, editing alongside someone in real > time, and who can see what. > **This one needs a second person.** If you are working alone, read it now to > know what is available and come back when you have someone to build with — > nothing later in the docs depends on having done it. --- ## Step 1: Check they can get in Your colleague needs to be in this workspace already. **The link alone does not grant access**: it opens the workflow for someone who is already a member, and shows nothing to anyone else. If they are not a member yet, invite them from the workspace menu → **Manage team**. See [Setting Up Your Team](/getting-started/setting-up-your-team) for the roles available and what each one may do. If you have restricted this particular workflow, check its visibility too — see [Workflow Visibility and Sharing](/build/core-concepts/workflow-visibility). --- ## Step 2: Share the link Copy the URL of your workflow from your browser's address bar and send it to your colleague. When they open it, their avatar appears in the top right corner and you will see their cursor moving across the canvas in real time. Anything either of you changes shows up for the other without a refresh. --- ## Step 3: Split the work The lead router from Tutorial 3 divides neatly between two people, which is the point of building it together rather than describing it afterwards. ### Take the routing yourself Configure the **Conditions** step: the rules that decide which region a lead belongs to. That is the part that needs to know how the workflow is wired. ### Give the copy to whoever owns it Your colleague clicks the four **Slack** steps and writes the message for each. That is the part that needs to know what Sales actually wants to say, which is rarely the same person. You are both editing the same workflow at the same time, so there is no copy to merge afterwards and no screenshot to describe. --- ## Step 4: Save a version before you leave it Once the two of you are happy with it, save a named version. That gives you a point to return to if the next round of edits goes wrong — see [Versioning](/build/core-concepts/versioning) for how restoring one behaves. --- ## What's Next? That covers the tutorials. The next step is building something for your own work. - **[Cookbook](/getting-started/cookbook/overview)** — worked recipes covering real business scenarios, with settings to copy. - **[Real-Time Editing](/build/the-canvas/real-time-editing)** — what multiplayer does when two people change the same step at once. - **[Glossary](/reference/glossary)** — look up any term these tutorials used. --- Source: https://docs.getglow.ai/getting-started/tutorials/lead-routing-tutorial # 3. Branching > Split a workflow into routes with a Conditions step, then merge them back into one record. A branching workflow sends each record down one of several routes and then brings them back together. You will build a lead router that sorts incoming leads by region and records every one of them in the same spreadsheet. Instead of placing every step by hand, you will use the **Workflow Assistant** to generate the structure, then wire the merge yourself. > **What you will learn:** Using AI for the first draft of a structure, > conditional branching, and merging branches back into one step. > **Before you start:** connect Slack and Google Sheets, create four Slack > channels for the routes, and prepare a Google Sheet with columns for name, > email, and region. If your company manages app approvals centrally, ask an > administrator to approve either connection before you begin. --- ## Step 1: Generate the Architecture Say you want to catch incoming leads from a Webhook and split them into four Slack channels by geographical region. ### Prompt the Assistant Start a fresh workflow, as in Tutorial 2: in the sidebar open **Automations** and click **Create new**. Then open the Workflow Assistant: click **Assistant** in the panel at the bottom right of the canvas, and type: > *"Create a webhook trigger. Then add a Conditions step with three routes (US East, US West and Europe), plus an Else route for everything else. Connect a Slack notification step to the end of each route."* ### Watch it Build Hit **Enter**. The assistant lays out the multi-branch structure on your canvas. ### Give the Webhook test data As in Tutorial 2, click the Webhook step, open **Test & Debug**, and edit the Testing data (the pencil icon, then Cmd+Enter to apply). Give it a sample lead so there is something to route: ```json { "name": "Ana Silva", "email": "ana@example.com", "region": "Europe" } ``` `region` is the field the Conditions rules will test. ## How a Conditions Step Routes A Conditions step is not a yes/no fork. You give it a list of named conditions and it checks **every one of them**, firing the branch of each that is true. Anything matching none of them takes the **ELSE** branch. That matters here. If a lead could satisfy both "US East" and "US West", both branches fire and the lead is announced twice. Write the rules so only one can match. And "Other" is not a fourth rule you write. It is the ELSE branch, which fires precisely when the other three did not. --- ## Step 2: Set the Rules Click the **Conditions** step to configure each route. A route's rule reads the field from the test data: the "Europe" route tests that the webhook's `region` equals `Europe`, picked from the data icon rather than typed. Do the same for the two US routes. The Else route needs no rules of its own — it fires only when none of the other three matched. While you are here, click each **Slack** step and write the message for its route. --- ## Step 3: Merge the Branches Whichever region a lead goes to, you want to record _all_ of them in a single Google Sheet at the end. ### Add Google Sheets Open **Apps** in the dock, search for Google Sheets, and pick the **Add Single Row** action. Place it on the far right side of your canvas. ### Configure it Click the step: choose your account (**Connect new account…** if none is connected yet), then the spreadsheet and the worksheet. Map each column from the webhook's fields with the data icon (name, email and region), the same move as Tutorial 2's message field. ### Merge the Branches Click the output terminal (the dot on the right side) of the "US East" Slack step, and drag a line to the input terminal of the Google Sheets step. Repeat this for the other three Slack steps. ### Set the merge mode Click the input terminal on the Google Sheets step and set it to **OR (Data from any step)**. The Google Sheets step then runs as soon as _any_ one of the Slack branches finishes, so every lead is recorded whichever route it took. **OR is the right choice whenever the branches are alternatives**, as they are here: a lead takes one route, so the step runs once. The other option, **AND (Data from all steps)**, is for branches that all run every time — a fan-out you deliberately split and want to bring back together once each side has finished. --- ## Step 4: Test & Deploy You have built a routing workflow that branches four ways and merges back to one record. Hit **Run** to test the logic. If you run into unexpected issues or missing data, see the [Error Handling Guide](/build/core-concepts/error-handling). Once it works, switch the workflow from **Draft** to **Live** so its trigger starts firing on its own. --- ## What's Next? 👉 **[Choose a Cookbook Workflow →](/getting-started/cookbook/overview)** - Building with a colleague? Continue with the optional [Working Together](/getting-started/tutorials/collaborating-on-a-canvas) guide. - Before relying on the workflow, read [Operating Workflows](/build/core-concepts/operating-workflows). --- Source: https://docs.getglow.ai/getting-started/tutorials/tutorial-basics # 2. The Basics: Build It Yourself > Build the same kind of workflow by hand: placing steps, connecting them, and mapping data between them. **About ten minutes.** This time you pass data from one step into another. Use Slack to send the result to a real app, or follow the no-account path with AI Prompt. > **What you will learn** > > - Placing steps on the canvas and connecting them > - Connecting an app so a workflow can act on your behalf, if you use Slack > - Mapping data from one step into another > **Before you start:** this tutorial connects Slack. In many companies > installing a Slack app needs an administrator's approval, so it is worth > checking first. If you cannot connect Slack, follow the **AI Prompt path** in > Steps 2–4. It needs no connected account and teaches the same mapping skill. --- ## Step 1: Add a trigger Start a fresh workflow for this: in the sidebar open **Automations** and click **Create new**, as in Tutorial 1. Every workflow starts with a trigger: the event that wakes it up. ### Replace the trigger placeholder On the empty canvas, click **+ Add Trigger** on the left-hand placeholder. The dock's catalogue opens. ### Add a Webhook Under **Tools → Start**, click **Webhook**. It takes the placeholder's spot on the canvas. ### Give it test data Click the step and open the **Test & Debug** tab. **Testing data** shows a sample object as a small structured editor. Hover over its top line and click the pencil icon: the object turns into editable text. Replace it with: ```json { "visitor_name": "Alice", "message": "I would like a demo!" } ``` Press **Cmd+Enter** (Ctrl+Enter on Windows) to apply. The editor folds back into the structured view with your two fields. ### Run it Click **Run step**, then open the **Executions** tab. You will see the two fields you just entered. Glow now knows the shape of the data this trigger produces. That is what makes the next step possible. > A trigger with no data is hard to build against. You would be mapping fields > you cannot see. Giving it test data first is the normal way to work, not a > shortcut for tutorials. --- ## Step 2: Add an action A trigger on its own does nothing. Choose the path that fits what you can connect. ### Add a Slack step Open **Apps** in the dock at the bottom, search for **Slack**, and pick **Send Message**. It lands on the canvas; place it to the right of your Webhook. ### Connect the steps Drag from the dot on the right edge of the Webhook step to the dot on the left edge of the Slack step. That line is the execution order: Webhook first, Slack second. ### Connect your account Click the Slack step and choose **Connect new account…** in its Account field. After authorising, pick the channel the message should go to. ### Add an AI Prompt step Open **AI** in the dock and choose **AI Prompt**. It needs no connected account. Place it to the right of your Webhook. ### Connect the steps Drag from the dot on the right edge of the Webhook step to the dot on the left edge of the AI Prompt step. --- ## Step 3: Map the data This is the part worth slowing down for. You will make the action use what arrived at the trigger rather than fixed text. Click the data icon beside the field you are editing to open the **Workflow data** panel, then pick `visitor_name` under the Webhook step. Glow inserts the reference with the Webhook's step number in front. The snippets below write that number as `1`. **Read the real number off your Webhook step** and use it instead, exactly as Tutorial 1 taught. In the Slack step, put this in the **Message** field: ```text New inbound request from {{ 1.visitor_name }}: {{ 1.message }} ``` In the AI Prompt step, put this in the **Instructions** field: ```text Write a one-line greeting for {{ 1.visitor_name }} that responds to this message: {{ 1.message }} ``` > **Why a number and not a name?** Because renaming a step never breaks > anything. `{{ 1.message }}` keeps working however you label step 1. Full > syntax in [Variable Reference Syntax](/reference/variable-syntax). --- ## Step 4: Run it, then set it Live ### Run the whole workflow Click **Run** in the dock. Glow executes both steps using the test data from Step 1. - **Slack path:** confirm the message arrived in your chosen channel. - **AI Prompt path:** open the AI Prompt step, select **Executions**, expand the latest run, and read its `result`. ### Set it Live Click the status control on the workflow (it currently reads **Draft**) and choose **Live**. In **Draft** the workflow only runs when you run it: _triggers disabled, manual runs only_. Once it is **Live**, anything that sends a request to the Webhook's URL starts it automatically. See [Webhook Triggers](/build/triggers/webhook) for how to point a real system at it. --- ## What's Next? You can now build a workflow by hand and move data through it. If you used Slack, you also connected an app that acts on your behalf. Next: branching, so a workflow can take different paths depending on what arrives. 👉 **[Continue to Tutorial 3: Branching →](/getting-started/tutorials/lead-routing-tutorial)** --- Source: https://docs.getglow.ai/getting-started/tutorials/your-first-workflow # 1. Quickstart: Your First Workflow > Build a working automation in about five minutes, using nothing but the browser. No accounts to connect, no code. **About five minutes.** By the end of this page you will have built an automation, all inside Glow. It runs on a schedule, asks AI to write something, and shows you what it wrote. You need nothing but a Glow account. No apps to connect, no command line, no code. > **What you will learn** > > - How a workflow is put together: a **trigger** and the **steps** that follow it > - How to place a step and open its settings > - How to run a workflow and read what it produced > - How steps are numbered, and why that matters Some of those words may mean nothing to you yet. That is fine. Each one is explained where you first need it, and the [Glossary](/reference/glossary) has the rest. ## The idea in three sentences A **workflow** is a sequence that starts when something happens and then does things in order. The thing that starts it is a **trigger**: a schedule, an incoming message, a new row in a spreadsheet. Everything after it is a **step**. Each step can use whatever the steps before it produced. That is the whole model. Everything else in Glow is a variation on it. --- ## Step 1: Create the workflow In the sidebar open **Automations** and click **Create new**, the yellow button above your workflows. (The **Create new** in the workspace menu makes a new workspace, which is not what you want here.) If you have not been in the app before, [Navigating the Dashboard](/getting-started/navigating-the-dashboard) shows where these are. A new canvas opens, ready for a trigger and a first action. The **Workflow Assistant** opens along the right-hand side. It can build workflows from a description, and Tutorial 3 uses it. For now close it with the **X** in its top right, so you have the whole canvas to work with. > Everything you place lands on the **canvas**. You find things to place in the > row of buttons along the bottom: **Apps**, **Tools**, **AI**, **Subflows**, > **Search**. That row is called the dock. ## Step 2: Add the trigger You will use **Scheduler**, which starts the workflow at a time you choose. It is the one trigger that needs nothing set up: no account, no address, no incoming message. ### Open the trigger placeholder Click the grey **Click to Add Trigger** placeholder on the canvas. The catalogue opens over it. ### Choose Scheduler Under **Start**, click **Scheduler**. It takes the placeholder's spot, already wired to the action placeholder beside it, and its settings open on the right. ### Look at the schedule **Schedule mode** is set to **AI Assistant**, and **Schedule description** already reads `Run everyday at 8:00 AM`. Leave both alone: that is a perfectly good schedule, and you will not wait for 8am to test it. > There is a **Manual Cron** mode beside it for people who already know cron > expressions. You will not need it today: describing the schedule in plain > words does the same job. **Next runs** shows you the times it worked out, so > you can check it means what you meant before you rely on it. ## Step 3: Add the AI step ### Open the action placeholder Click the grey **Click to Add Action** placeholder, and in the catalogue that opens pick the **AI** section. ### Choose AI Prompt The top group is **Glow AI**, marked _no API keys required, zero token costs_. Click **AI Prompt** there. It takes the placeholder's spot, wired to your Scheduler. The providers listed below it (OpenAI, Anthropic, and the rest) each need an API key of your own. You do not need one today. ### Write the instruction The **Instructions** field arrives with a sample in it. Replace it with: ```text Write a two-sentence motivational note to start the working day. ``` > **Instructions is the whole of the step.** Whatever you write there is what > the AI is asked to do. Vague instructions produce vague results, which is the > single most common reason an AI step disappoints. ## Step 4: Notice the step numbers Look at the number on each step. They may well not be 1 and 2. That surprises everybody the first time. A step's number is fixed when the step is created and stays with it: it is the step's name, not its position in the run. Numbers are not handed out again once used, so gaps are normal and the first step is not always 1. This matters because **steps refer to each other by number**. To use the AI step's answer somewhere later, you would write `{{ 4.result }}`, where `4` is whatever number is actually on the step. > **Read the number off the step rather than assuming it.** Pointing at a step > number that does not exist stops the run, and the reason is recorded on the > step: _"Placeholder 99.nothing didn't find data"_. Copying the number from the > step avoids it, as does the data icon beside every field, which lists earlier > steps' output to pick from (Tutorial 2 uses it). ## Step 5: Run it Click **Run** at the left of the dock. Glow runs the workflow once, immediately, without waiting for 8am. Each step picks up a small counter showing how many times it has run. ## Step 6: Read what it wrote This is the part everybody gets stuck on, so it is worth being precise. ### Open the step Click the **AI Prompt** step to open its settings. ### Switch to Executions Click the **Executions** tab. You will see one row per run: a relative time like _"a few seconds ago"_ and a long identifier. ### Expand the run **Click the small arrow at the left of the row.** The row expands to show what the step produced: ```text result: Embrace this new day with renewed energy, knowing that your unique skills and dedication make a real difference… ``` **That is the moment.** A model you never configured and never gave a key to wrote that, inside a workflow you built in about five minutes. > Note the word **`result`**. That is the name the answer is stored under, which > is why a later step would reach it as `{{ 4.result }}`. Every AI step in Glow > returns its answer the same way. Run it a second time and expand the new row. The note is different: same instruction, new wording. If you want it shorter, or warmer, or signed off in your name, edit the Instructions and run it again. Getting a prompt right by iterating on it is most of the work in any AI step. ## What you just built A trigger that fires on a schedule, and a step that produces something new each time. Every workflow in Glow is that shape, with more steps in between. Three things are worth carrying forward: - **You never left the browser.** No terminal, no connected accounts, no API keys. - **Steps are referenced by number**, and the number comes off the step itself rather than from counting. - **A run's output lives in Executions**, behind the arrow on the row. ## Making it real Right now this workflow writes something and stops, and it only runs when you click **Run**. Two changes make it live: - **Set it to Live.** A new workflow starts in **Draft**: the schedule is off and it runs only when you run it yourself. Click the status control next to the workflow's name (it currently reads **Draft**) and switch it to **Live**. The trigger starts firing on its own. - **Send the result somewhere.** A note nobody reads is not much use. Add a step after the AI one that posts to Slack or sends an email, and reference the answer as `{{ 4.result }}`. That second change needs a connected account, which is exactly what the next tutorial covers. ## What's Next? 👉 **[Continue to Tutorial 2: Build It Yourself →](/getting-started/tutorials/tutorial-basics)** to connect a real app and pass data between steps. - Unsure about a term? The [Glossary](/reference/glossary) defines everything used here. - Know the outcome but not the Step? Use [Choose the Right Step](/build/which-step). --- Source: https://docs.getglow.ai/getting-started/welcome # Welcome to Glow > Build reliable workflows with your team, use AI where judgement helps and operate client automation in separate workspaces. **Build together in real time. Operate every client in a separate workspace.** Glow is visual workflow automation where your team can design, test and operate on one shared canvas. Keep exact work deterministic and add AI where the input needs judgement. ## The Glow difference - [Build together in real time](/build/the-canvas/real-time-editing): Mia Route priority leads Sam · to Sales AI Prompt Adding Step… Teammates edit the same workflow at the same time and see each other's changes as they happen. - [Operate every client in a separate workspace](/msp/how-multi-tenancy-works): Consultants and MSPs can build, deploy and monitor across governed client workspaces without mixing workflows, accounts or run history. - [Build together while the Assistant works](/build/ai-features/workflow-assistant): Describe the process in plain language. The Workflow Assistant builds on the live canvas while you and your teammates keep working. - [Combine rules with AI Steps & Agents](/build/ai-features/overview): Deterministic AI Keep exact work deterministic. Drop AI Prompts, data transforms or an autonomous AI Agent Step onto the canvas wherever a task needs judgement. For the full product model, continue to [What Is Glow?](/getting-started/what-is-glow). ## Choose your starting point - [Build your first workflow](/getting-started/tutorials/your-first-workflow): **New to Glow? Start here.** Build, run and inspect a workflow in about five minutes. You do not need to connect an app or write code. - [Start from an outcome](/build/which-step): Choose a workflow pattern, then find the trigger, logic and action Steps that fit the result you need. - [Move an existing automation](/getting-started/migrating-to-glow): Map concepts from your current platform onto Glow, import what you can and finish the workflow safely. - [Build for clients](/msp/overview): Set up governed access, work inside separate client workspaces and follow the first-client runbook. ## Learn by building The core tutorial sequence moves from a blank canvas to a tested workflow: 1. **[Quickstart: Your First Workflow](/getting-started/tutorials/your-first-workflow)** — place a trigger and an AI Step, run them and inspect the result. 2. **[Build It Yourself](/getting-started/tutorials/tutorial-basics)** — connect a real app and map data between Steps. 3. **[Branching](/getting-started/tutorials/lead-routing-tutorial)** — add routing, test each path and switch the workflow to Live. Building with a colleague? **[Working Together](/getting-started/tutorials/collaborating-on-a-canvas)** is an optional guide to sharing and editing the same workflow in real time. ## Go further - **[Cookbook & Use Cases](/getting-started/cookbook/overview)** contains complete business workflows — from [AI Support Router with Tools](/getting-started/cookbook/ai-customer-support-router) to [HubSpot Alerts](/getting-started/cookbook/hubspot-slack-alerts). - **[Public Templates](/getting-started/templates/overview)** gives you a starting canvas instead of an empty one. - **[Operating Workflows](/build/core-concepts/operating-workflows)** covers controlled testing, switching to Live and checking the first automatic runs. - **[Troubleshooting](/reference/troubleshooting)** starts from the symptom when a route or output differs from what you expected. ## What's Next? - 👉 **[Build Your First Workflow →](/getting-started/tutorials/your-first-workflow)** - Coming from another platform? Follow [Migrating to Glow](/getting-started/migrating-to-glow). - Building for several companies? Start with [Working With Clients](/msp/overview). --- Source: https://docs.getglow.ai/getting-started/what-is-glow # What Is Glow? > How Glow connects triggers, logic and actions on a shared canvas, with selective AI and separate client workspaces. Glow is a visual workflow automation platform for teams that build together. It connects the apps and data behind a business process, runs the work automatically, and gives consultants and MSPs a separate governed workspace for each client. A workflow might start when a form is submitted, on a schedule, or when a record changes in a connected app. On the canvas, a [trigger](/build/core-concepts/triggers-and-actions) starts the run, the steps after it make decisions and take action, and each step can use data produced earlier.  *A workflow on the canvas. The trigger is on the left; each step is numbered, and the numbers are how one step refers to another's output.* , }, { value: "Trigger → action", label: "A model that stays visible", note: "Add logic between the two", icon: , }, { value: "Selective AI", label: "For judgement and assistance", note: "Keep exact rules deterministic", icon: , }, { value: "Per client", label: "Workspaces for service teams", note: "Separate data and governed access", icon: , }, ]} /> --- ## The shape of every workflow Every automation in Glow starts with a trigger and continues through steps that do the work. Add logic between them when the process needs to transform, route or repeat data: A workflow stays readable because that model remains visible on the canvas. You can inspect what each step received, what it returned and which route the run took. --- ## What makes building in Glow different ### Your team works on the same live canvas Several people can open and edit one workflow at the same time. Their cursors show where they are working, and changes appear for everyone as they happen. Different fields can be edited without one person waiting for another to finish. That makes the canvas useful for more than initial construction. A team can investigate a run together, explain a process to its owner, or divide a larger workflow into branches without passing files around. See [Real-Time Editing](/build/the-canvas/real-time-editing). ### The Workflow Assistant builds where you build Describe an automation in plain language and the [Workflow Assistant](/build/ai-features/workflow-assistant) places, connects and configures steps on the same canvas. You can keep editing while it works, then review and test what it produced before switching the workflow to Live. The Assistant is for creating and changing the workflow. AI steps inside the workflow are different: they make a judgement each time a run reaches them. ### AI is one part of a reliable workflow Use regular steps when the rule can be written down and the same input should produce the same result. Use an AI step when the work requires interpretation, such as classifying a support request or extracting fields from an unfamiliar document. The common design uses both. AI interprets the uncertain input; ordinary steps route the answer and perform the final action. Add [Human Review](/build/action-steps/user-approval) before an important write or send when a person should approve the result. ### Client work stays in the client's workspace Consultants and MSPs can work across separate client workspaces rather than combining every customer's automation and credentials in one account. Each workspace holds its own workflows, connected accounts, secrets, files and run history. Access is governed through a grant the client can review, narrow or end. When you deploy a workflow to clients, each selected workspace receives an independent Draft copy for its own accounts and review. See [How Multi-Tenancy Works](/msp/how-multi-tenancy-works). --- ## The building blocks you will use - **Triggers** start a workflow from a [webhook](/build/triggers/webhook), [schedule](/build/triggers/scheduler) or event in a connected app. - **Actions** send messages, update records and call the services where work happens. - **Control flow** uses [Conditions](/build/action-steps/conditions), [Switch](/build/action-steps/switch) and [loops](/build/action-steps/loops) to choose and repeat work. - **Data mapping** carries a value from one step into another. [Data Transformation](/build/core-concepts/data-transformation) changes that value as it moves. - **Draft and Live** separate manual testing from automatic triggers. [Versions](/build/core-concepts/versioning) give you a known state to restore. - **Execution details** show the route, input and output of each run so you can verify and troubleshoot it. ## Where AI belongs A useful rule is to ask whether the decision can be written down. For example, an AI step can classify an incoming support ticket. A Conditions step then applies an exact routing rule, Slack alerts the right team, and the help desk record is updated. Only the classification needs AI; the rest stays predictable. --- ## What you can build - [Lead Routing](/getting-started/tutorials/lead-routing-tutorial): Receive a lead, route it by an exact business rule and bring the branches back into one record. - [Support Ticket Triage](/getting-started/cookbook/zendesk-sentiment-triage): Use AI to classify the request, then apply exact rules to update the ticket and alert a person when needed. - [Invoice Processing](/getting-started/cookbook/ai-invoice-processing): Extract structured fields from a document, pause for approval and write each approved line to its destination. - [Client Automation](/msp/first-client-workflow): Work inside a client's separate workspace, test with their accounts and leave the workflow ready for the client to own and operate. --- ## What's Next? - 👉 **[Build Your First Workflow →](/getting-started/tutorials/your-first-workflow)** - Choose a workflow shape or Step family in [Choose the Right Step](/build/which-step). - Learn the underlying model in [What Is a Workflow?](/build/core-concepts/what-is-a-workflow). --- Source: https://docs.getglow.ai/manage/apps-and-integrations/connecting-an-app # Connecting an App > Create a connection to a third-party service, so its steps can act on your account without you handling credentials in the workflow. A connection stores your credentials for a third-party service securely, so Glow can call that service's API on your behalf. You create one before a workflow can use the service. ## Navigating to the Connections Page Open **Connections** from the left sidebar of the Glow dashboard. The page shows one card per app, so an app you have already signed in to sits alongside the ones you have not.  *Each card shows the app, its category, and whether an account is connected. Use the My connections filter to narrow the list to apps you already use.* ## Adding a New Connection 1. On the **Connections** page, type the app's name into the search box (for example, "Google Sheets"). 2. Click **Connect** on that app's card. 3. Complete the authentication flow in the window that opens. The method depends on the app: see below. 4. Glow tests the credentials as you finish. On success the card shows the account you signed in with. ## Authentication Types Glow supports several authentication methods depending on the app you are connecting. ### Signing in to the service (most apps) Most major platforms (Google, Slack, Salesforce, etc.) use OAuth. When you select one of these apps: 1. A popup window opens, directing you to the app's sign-in page. 2. Sign in with your credentials for that service. 3. Review and approve the permissions Glow is requesting. 4. The popup closes automatically and your connection is confirmed. > **Tip:** If the popup is blocked by your browser, check your popup blocker > settings and try again. ### API Key Some services authenticate via API key. When prompted: 1. Log in to the third-party service's dashboard. 2. Navigate to the API or developer settings to generate or copy your API key. 3. Paste the key into the Glow connection form. 4. Click **Save**. ### Username and Password A small number of services use basic authentication: 1. Enter your username and password for the service. 2. Click **Save**. ### Custom Authentication Some integrations require additional fields such as subdomain, region, or token. Glow displays the required fields dynamically based on the app you selected. Fill in all required fields and click **Save**. ## Selecting an Account in the App drawer When you add an app step to a workflow, its App drawer includes an **Account** dropdown. This dropdown lists all connections you have created for that app. 1. Click the **Account** dropdown in the App drawer. 2. Select the connection you want to use for this step. 3. To sign in to another account without leaving the editor, choose **Connect new account…** from the same dropdown. It is always offered, so this is also how you add a second account for an app you have already connected. ## Practical Example: Connecting Google Sheets 1. Open **Connections** and search for "Google Sheets". 2. Click **Connect** on its card. 3. An OAuth popup opens. Sign in with your Google account. 4. Review the permissions (Glow will request access to read and write your spreadsheets). 5. Click **Allow**. 6. The popup closes, and the Google Sheets card now shows the address you signed in with. 7. In your workflow, add a Google Sheets step. In the step configuration, select your Google account from the **Account** dropdown. ## From Connected to Configured: One Full Action Authentication is half the job. Here is the same Google Sheets connection carried through a working **Add Single Row** step, which is the pattern every app action follows: 1. Open **Apps** in the dock, search for **Google Sheets**, and pick **Add Single Row**. It lands on the canvas; connect it after the step whose data it should record. 2. Click the step. Choose your account, or **Connect new account…** if none is connected yet. 3. Pick the **Spreadsheet**, then the **Worksheet**. Both dropdowns read from your account, so what you see is what exists. 4. Map the columns. Each column of the sheet appears as a field: click the data icon beside one to open the **Workflow data** panel and pick the value from an earlier step. Mixing picked values with typed text is fine: a field can read `{{ 1.name }} ({{ 1.email }})`. 5. Run it once and open the step's **Executions** tab. The output shows what was written, and the row is in your sheet. The names change per app: a Slack step wants a channel where Sheets wants a worksheet. The sequence is always account, then target, then field mapping with the data icon. ## Reading Data Instead of Writing It The same connection reads as well as writes. Where **Add Single Row** puts a row in, **Get Values in Range** takes rows out, and it is the step to reach for when a workflow starts from what is already in a sheet: 1. Open **Apps**, search for **Google Sheets**, and pick **Get Values in Range**. 2. Choose your account, then the **Spreadsheet** and the **Worksheet**, exactly as above. 3. Leave **Range** empty to get everything, or narrow it with A1 notation such as `A1:E50`. The field is optional. 4. Run the step once, then open its **Executions** tab and read the output. The values arrive under `{{ N.ret }}`, where `N` is the step's number, and the Executions tab shows the real field names to build against. Reading first is what makes a "go through the rows and do something with each" workflow possible: feed the output into a step set to **Each item**, or into a [Repeater](/build/action-steps/loops) if each row needs several steps. **Looking for one specific row** rather than a range? **Find Row** searches a column for a value and returns the match, which saves pulling the whole sheet in and filtering it afterwards. ## Testing a Connection After creating a connection, Glow runs an automatic test to verify that the credentials are valid. If the test fails: - Double-check that you entered the correct credentials. - For OAuth connections, ensure you approved all required permissions. - Verify that the third-party service is not experiencing downtime. There is no separate re-test action. To inspect a connection, click **Details** on its card. If the stored credentials have stopped working, the step that uses the connection reports it on its next run. ## Edge Cases and Troubleshooting ### Expired tokens OAuth tokens can expire. Glow refreshes them automatically using the stored refresh token. A refresh can still fail, for example when you revoke Glow's access from the service itself. You then find out in the workflow rather than on the Connections page. Open the step that uses the account, and the App drawer names the broken connection, with a **Reconnect** button beside it. Clicking it re-runs the sign-in and re-points the step at the refreshed connection. ### Re-authentication You may need to re-authenticate a connection if: - You changed your password on the third-party service. - You revoked Glow's access from the third-party service's settings. - The third-party service rotated or invalidated your API key. - Your organization's security policy requires periodic credential rotation. To re-authenticate, open a step that uses the account and click **Reconnect** in the App drawer. That walks you through sign-in again and points the current step at the refreshed connection. Open the other workflows that depend on the account and confirm their connection before returning them to service. ### Popup issues If the OAuth popup fails to open or closes prematurely: - Disable your browser's popup blocker for the Glow domain. - Try a different browser. - Clear your browser cache and cookies, then try again. ## What's Next? - Keep your connections healthy over time with [Managing Connections](/manage/apps-and-integrations/managing-connections). - Understand what Glow stores and how it is protected in [Credentials & Authentication](/manage/apps-and-integrations/credentials). --- Source: https://docs.getglow.ai/manage/apps-and-integrations/credentials # Credentials & Authentication > How to securely connect your apps to Glow using OAuth and API keys. A **connection** gives Glow permission to act on your behalf in a third-party service such as Slack, Google Drive, or your own internal API. It stores the credentials that service requires. Glow encrypts your tokens at rest with AES-256. Once a key is saved, it can never be read back through the interface. --- ## Types of Connections Most connections use one of two methods, depending on the service you are connecting. [Connecting an App](/manage/apps-and-integrations/connecting-an-app) lists the full set, including username/password and custom fields. ### 1. OAuth 2.0 (Recommended) Most modern SaaS apps use OAuth. Connect an app via OAuth and you are redirected to the app's website, such as Google or Microsoft. There you log in and grant Glow specific permissions. - **Advantage:** You never hand over your actual password to Glow. Instead, Glow receives a secure "token" that it uses to authenticate. - **Management:** Glow automatically handles token expiration and background refreshing for you. ### 2. API Keys For developer-focused tools or custom HTTP requests, you may need to provide an API key. When creating a credential of this type, you paste the key from the third-party service into Glow. --- ## How to Add a Credential You can add a new credential in two ways: ### From the Canvas While Building Add an action step to the canvas, such as **Send Slack Message**, and open the App drawer. Open the **Account** dropdown and choose **Connect new account…** to complete authentication without leaving the canvas. ### From Connections If you want to pre-configure connections for your team: 1. Open **Connections** in the workspace sidebar. 2. Search for the service you want and click **Connect** on its card. --- ## Security & Encryption When you save an API key or when Glow receives an OAuth token, the data is encrypted immediately before being stored in our database. - **AES-256 Encryption:** All sensitive credential data is encrypted at rest using industry-standard AES-256 encryption. - **No Plaintext Access:** Once an API key is saved, it can never be viewed in plaintext again, not even by you or your workspace admins. It is only used by Glow when a workflow runs. - **Least permissions:** When using OAuth, Glow only requests the minimum permissions (scopes) needed for the actions in our integration catalog. --- ## Managing and Revoking Access If an API key is compromised, or an employee leaves your organization, you can revoke access straight away. 1. Open **Connections** in the workspace sidebar. 2. Find the app's card. 3. Click **Disconnect** and confirm. Disconnecting takes effect on the next run. A step that used the account fails at that step, and the App drawer flags the account as no longer authorized with a **Reconnect** button. Point each affected step at a replacement account, or reconnect with fresh credentials. See [Managing Connections](/manage/apps-and-integrations/managing-connections). ## What's Next? - Store non-OAuth API keys outside your step configuration in [Secrets and Variables](/manage/workspace-settings/secrets-and-variables). - Find and repair the workflows a revoked credential breaks in [Managing Connections](/manage/apps-and-integrations/managing-connections). --- Source: https://docs.getglow.ai/manage/apps-and-integrations/managing-connections # Managing Connections > Monitor, update, and organize every app you have connected to Glow, from one central page. The **Connections** page is the central place to find apps, add your own accounts and manage the connections you created.  *Connected apps show the account in use; unconnected ones show Not connected. Use My connections to filter down to just the apps you have set up.* ## Viewing All Connections Open **Connections** from the left sidebar. The page shows one card per app. A card gives you the app's name, its category, and either the connected account's email address or **Not connected**. Three controls narrow the page down. **My connections** filters to the apps you have already set up, the search box finds one by name, and the view toggle switches between grid and list. ## Connection Status A card reads one of two ways: the connected account, shown in green, or **Not connected**. The card names the account; whether its access still works is checked where it matters, at the step that uses it. A step whose account has lapsed names it and offers a **Reconnect** button in the App drawer. ## Disconnecting a Connection Only the person who created a connection can disconnect it. Other workspace members can use an available connection in a workflow, but they do not see its lifecycle actions. 1. Find the app's card on the **Connections** page. 2. Click **Disconnect**. 3. Confirm. > **Tip:** Before disconnecting, check which workflows use this account. > Disconnecting causes any step that references it to fail on its next run. ## Reconnecting Glow does not flag a dead connection on the Connections page. You find it in the workflow instead. Open the step that uses the account, and the App drawer names the broken connection, "<account> is no longer authorized", with a **Reconnect** button beside it. Clicking it re-runs the sign-in and re-points the step at the refreshed connection. ## Multi-Account Support Glow allows you to connect multiple accounts for the same app. This is useful when: - You manage workflows for different clients, each with their own Google Workspace. - Your team uses separate Slack workspaces for internal and external communication. - You need to read from one Salesforce org and write to another. ### Adding a Second Account The quickest route is from inside a step. Open the **Account** dropdown in the App drawer and choose **Connect new account…**, which is offered whether or not you already have one. Sign in with the second account, and the step points at it straight away. ### Selecting Between Accounts in a Workflow Configure a step for an app with multiple connections and the **Account** dropdown lists every one of them. Select the one appropriate for that particular step. For example, say you have two Google Sheets connections, one for `operations@company.com` and one for `marketing@company.com`. One step can read from an operations spreadsheet while another writes to a marketing spreadsheet, all within the same workflow. ## Where Connections Appear in the App drawer When you add or edit an app step, its App drawer includes: 1. **Account**: a dropdown listing all connections for the selected app. This is where you choose which account to use. 2. **Action**: the specific operation to perform (for example, "Add Single Row" in Google Sheets). 3. **Configuration fields**: the inputs required by the selected action, which may reference data from previous steps. The **Account** dropdown always offers **Connect new account…** at the bottom, so you can sign in to a new account without leaving the workflow editor. ## Best Practices - **Know which account is which before you connect it.** A connection is labelled with what the provider returns, usually the account's email address. Signing in with an address that makes its purpose obvious (`billing@`, `alerts@`) is what tells two connections apart later. Where a provider returns no address at all, the card reads "Connected". That is another reason to connect accounts one at a time, in an order you will recognise. - **Review connections periodically.** Remove connections that are no longer in use to keep your Connections page clean and keep the list to what you actually use. - **Coordinate with your team.** A connection can be selected in workflows across the workspace, but only its owner can reconnect or disconnect it. Disconnecting affects every workflow that references it. ## What's Next? - Add a service you have not authenticated yet in [Connecting an App](/manage/apps-and-integrations/connecting-an-app). - See how workspace roles, workflow visibility and delegated access fit together in [Permissions and Access](/manage/workspace-settings/permissions-and-access). --- Source: https://docs.getglow.ai/manage/apps-and-integrations/overview # Apps & Integrations > The pre-built connections, what each one can do, and how to authorise the account a step acts through. An app connection lets a step act inside another service, using an account you have signed in to: it sends the Slack message, or adds the spreadsheet row. You sign in once, and every step that needs that service can use it. Glow provides a broad catalogue of pre-built business app connections. The [Integrations Directory](https://getglow.ai/integrations) is the current list of available apps, triggers, and actions. ## The Integrations Directory To see what a given app can do before you build with it, search the **[Integrations Directory](https://getglow.ai/integrations)**. It lists every trigger and action Glow supports for each service. > **Search the Directory:** 👉 > **[https://getglow.ai/integrations](https://getglow.ai/integrations)** ### How to use the directory: - **Search any app:** Type the name of the tool you use (e.g., Salesforce, Slack, Notion). - **View available actions:** Click on any app to see the exact triggers and actions Glow supports out of the box for that specific tool. - **Request an app:** If the app you need is not listed, submit a request from the search bar. ## Using your own API keys Most apps connect over OAuth, and Glow stores and refreshes those tokens for you. Where a service authenticates with an API key instead, you supply your own key in the connection settings. That usage is billed to your account and covered by your own agreement with the provider. Keys are stored encrypted. See [Credentials](/manage/apps-and-integrations/credentials). Glow's [AI steps](/build/ai-features/supported-models) are the exception: they run on managed infrastructure and need no key at all. ## How apps work in Glow When you drag an app onto the canvas, Glow provides two layers of functionality: 1. **Authentication Handling:** Glow securely stores your OAuth tokens, API keys, and passwords, handling token refreshes automatically. 2. **Pre-built Actions:** You get a clean UI with dropdowns and input fields specific to that app, rather than raw HTTP requests. Examples: "Send Slack Message", "Update CRM Record". ## What's Next? - Sign in to your first app in [Connecting an App](/manage/apps-and-integrations/connecting-an-app). - See what to do when an account stops working in [Managing Connections](/manage/apps-and-integrations/managing-connections). - Call a service Glow has no integration for with the [HTTP Request](/build/action-steps/http-request) step. --- Source: https://docs.getglow.ai/manage/billing/credits-and-allowances # Step Credits, Tokens & Storage > Detailed consumption rules for workflow Step credits, hosted Glow AI model tokens, and persistent file storage. Every automation in Glow draws from three transparent meters: **Step credits**, **Glow AI tokens**, and **Retained storage**. Understanding how these meters operate allows you to accurately forecast costs and design efficient automations. --- ## 1. Step Credits Step credits measure standard workflow execution. Each step reserves capacity before execution and settles based on the actual work performed. 1. Step Triggered → 2. Atomic 1-Credit Reservation → 3. Execution & Settlement ### Execution outcomes and fair charging Glow uses a state-safe reservation lifecycle so you are charged only for actual work performed: **Outcome: Success (`pass`) · 1 Step Credit Deducted** The step completed all work successfully. The initial 1-credit reservation is settled and deducted from your monthly allowance. - **Charge:** **1 Step credit**. - **Settlement state:** `Settled`. - **Real-world scenario:** A Slack message, CRM update, or HTTP Request sent and acknowledged with `200 OK`. **Outcome: Pre-Work Error (`FailedUncharged`) · 0 Credits (Free)** The step failed before performing external chargeable work (e.g. invalid input mapping, unconfigured connection credentials). The reservation is released immediately. - **Charge:** **0 credits (Free)**. - **Settlement state:** `Released`. - **Real-world scenario:** An API step with an invalid authentication token fails validation before dispatching to the external service. **Outcome: Partial Batch Execution (`FailedChargeable`) · Proportional Settlement** For batch loops ([Run for each item](/build/action-steps/loops/run-for-each-item) or [Repeater](/build/action-steps/loops/repeater)), credits settle based on the exact number of item passes initiated before an error stopped the loop. - **Charge:** **Actual completed item invocations**. - **Settlement state:** `Proportionally settled`. - **Real-world scenario:** In a 100-item loop, 45 items successfully run before an external rate limit (429) halts the run → 45 credits settled. **Outcome: Allowance Exhausted · 0 Credits (Free)** If your monthly balance reaches 100%, the step is not admitted to the execution queue. It halts safely with `out_of_credits` without consuming any credits. - **Charge:** **0 credits (Free)**. - **Settlement state:** `Halted without charge`. - **Real-world scenario:** A webhook fires when monthly credits are depleted → the step halts safely with 0 charge. --- ### Loops and batch processing Workflows that iterate over collections of items meter each item pass individually: **Single-step loop execution:** - The step reserves credits equal to the total item count. - Upon completion, credits settle based on the actual items processed. - **Worked example:** A Slack step sending messages to 150 customer rows uses **150 Step credits**. **Multi-step loop body execution:** - Each step inside the loop body meters its executions per iteration. - **Worked example:** A loop body with 3 steps (Enrich Data → AI Prompt → Update CRM) running over 20 items consumes up to **60 Step credits** (3 steps × 20 passes), plus any tokens used by the AI step. **Modular child workflow execution:** - The parent step consumes **1 Step credit** to initiate the child execution. - The child workflow executes independently, with its internal steps metered normally. - Returning data back to the parent workflow incurs no additional charge. > **Batch admission protection:** Before starting a large batch, Glow verifies > that your workspace has sufficient remaining credits for the full list. If a > batch exceeds your available monthly balance, the step pauses safely rather > than partially executing and draining your account. --- ## 2. Glow AI Tokens Hosted AI features—including **AI Prompt**, **AI Agent**, **AI Data Transform**, and **Workflow Assistant**—draw from your workspace's **Glow AI tokens** balance. ```text Total Billable AI Tokens = Input Tokens + Generated Output Tokens + Reasoning Tokens ``` - **Input tokens:** Prompt instructions, schema definitions, and context documents sent to the model. - **Output tokens:** The generated response, JSON object, or text produced by the model. - **Reasoning tokens:** Internal step-by-step thinking tokens generated by advanced reasoning models. - **Autonomous agent loops:** In multi-step agent executions, every reasoning turn and tool-decision call meters its individual token consumption. ### Hosted Glow AI vs. Custom Provider Keys **Managed Glow AI:** - **API Keys:** None required; fully managed by Glow. - **Metering:** Token usage deducts directly from your workspace's **Glow AI tokens** allowance. - **Step Credits:** Consumes standard Step credits for canvas execution + tokens for model generation. **Third-Party API Keys (OpenAI, Anthropic, etc.):** - **API Keys:** You provide your own external API key in the step configuration. - **Metering:** Consumes **1 standard Step credit** in Glow. - **Token Costs:** Token consumption is billed directly to your third-party account by the model provider (0 Glow AI tokens deducted). --- ## 3. Retained File Storage Workspaces store uploaded attachments, CSV datasets, generated PDFs, and integration assets in **Retained file storage**. | Model Attribute | Policy | Operational Behavior | | :-------------------- | :------------------------- | :------------------------------------------------------------------------ | | **Standing Quota** | Measured in Gigabytes (GB) | Continuous workspace storage allocation. | | **Reset Schedule** | Never resets | Retained capacity persists across billing cycles. | | **Quota Checks** | Pre-upload verification | Validates available disk quota before storing files. | | **Capacity Handling** | Fail-safe retention | Existing files remain accessible; new uploads pause until space is freed. | - **Standing Capacity:** Unlike Step credits and AI tokens, file storage represents physical cloud disk volume and does not reset each month. - **Persistent Files:** Stored files remain permanently available until deleted by workspace team members. - **Upload Quota Checks:** Before committing a new file upload or file-generation step, the system verifies that the resulting total size fits within your allocated storage. - **Over-Capacity Behavior:** If storage reaches capacity, existing files remain safe, readable, and downloadable. New uploads pause until unused files are deleted or storage capacity is increased. --- ## Real-world calculation examples | Workflow Scenario | Configuration | Monthly Runs | Estimated Resource Consumption | | :------------------------------ | :---------------------------------------------- | :----------------- | :--------------------------------------------------------------------- | | **Lead Routing & CRM Alert** | 4 steps (Webhook → Filter → HubSpot → Slack) | 5,000 leads / mo | **20,000 Step credits**0 AI tokens · < 0.1 GB storage | | **AI Customer Support Triage** | 3 steps (Webhook → AI Prompt → Zendesk Reply) | 2,500 tickets / mo | **7,500 Step credits**~3.5M Glow AI tokens · < 0.5 GB storage | | **Daily CSV Report Processing** | Scheduler → Fetch CSV (500 rows) → Loop 2 steps | 30 days / mo | **30,060 Step credits**0 AI tokens · ~2.0 GB storage | ## What's Next? - 👉 **[Purchasing & Changing Amounts →](/manage/billing/subscription-plans)**: Select tailored resource amounts for your team. - **[Invoices & Payment Methods](/manage/billing/invoices-and-payments)**: Download past invoices, receipts, and manage billing contacts. - **[Usage Controls & Recovery](/manage/billing/usage-controls-and-recovery)**: Configure automated alerts, safeguards, and recovery workflows. --- Source: https://docs.getglow.ai/manage/billing/invoices-and-payments # Invoices & Payment Methods > How to manage corporate payment methods, update billing contact details, download invoices and receipts, and pay outstanding balances. Workspace Admins can manage corporate payment methods, verify billing notification contacts, review detailed invoice breakdowns, and export operational usage logs from the **Billing** tab. > **Strict Administrative Privacy:** Financial details, payment instruments, tax > registrations, and invoice documents are restricted to workspace **Admins**. > Workspace Members and Managers can view operational usage metrics but cannot > access financial records. --- ## Managing payment methods Glow processes credit card and corporate payment methods through an encrypted, PCI-compliant billing portal. ### Open Plan & Payment Navigate to **Billing & Usage → Billing** as an Admin and locate the **Plan and payment** section. ### Access the secure billing portal Select **Manage payment method**. You will be redirected to the secure portal where you can: - Add a new primary corporate credit card. - Update expiration dates and billing addresses. - Remove outdated payment cards. ### Return to Glow Once updated, the portal automatically returns you to Glow, and your refreshed card brand and last four digits will display immediately. --- ## Updating the billing contact Billing receipts, payment confirmations, renewal notices, and invoice alerts are automatically dispatched to the registered billing contact. ### Open billing contact dialog In **Billing & Usage → Billing**, select **Change billing contact**. ### Enter updated contact details Provide the new contact's full name and email address (for example, `accounting@yourcompany.com`). ### Verify email address Select **Send verification**. Glow sends an authentication link to the new address. Once the recipient confirms the link, the billing contact is updated across all future invoice communications. --- ## Invoice history and receipts The **Invoices and exports** section maintains a complete historical ledger of every subscription payment, capacity adjustment, and renewal transaction. | Invoice Date | Period Covered | Status & Amount | Available Documents | | :------------------- | :------------------- | :-------------- | :-------------------------------------- | | **Current Period** | Active Billing Cycle | Paid | PDF Invoice, Payment Receipt, Usage CSV | | **Previous Periods** | Historical Months | Paid | Downloadable Archive & Historical Logs | For each transaction, Admins can access: - **Invoice breakdown:** An itemized modal displaying the exact breakdown of Step credits, Glow AI tokens, and storage capacity, alongside applicable taxes and discounts. - **Hosted invoice:** An interactive web invoice suitable for online accounts payable workflows. - **PDF invoice:** A formal downloadable PDF document complete with company name, address, and registered tax identifier (VAT/GST). - **Payment receipt:** Instant confirmation receipt verifying the successful card transaction. - **Usage CSV export:** A detailed CSV log of the billable events and operations processed during that billing cycle. --- ## Paying outstanding invoices If a renewal charge or capacity expansion payment fails (for example, due to an expired card or bank hold): 1. Invoice Due → 2. 7-Day Grace → 3. Alert Banner → 4. Pay Now → 5. Active 1. The affected invoice displays an **Open** or **Action Required** badge in your invoice list. 2. A prominent alert banner appears across **Billing & Usage** indicating the outstanding amount and the payment deadline. 3. Select **Pay now** next to the affected invoice to open the payment dialog. 4. Confirm payment using your stored payment method or enter a new card. Once processed, the invoice transitions to **Paid** and active service continues without interruption. --- ## Sponsored and agency workspaces For agencies, IT consultants, and Managed Service Providers (MSPs) operating under Glow's multi-tenant architecture: - **Client privacy:** When an MSP sponsors or manages a client workspace, the client team sees operational meters on the **Usage** tab. - **Payer isolation:** The **Billing** tab in the sponsored workspace confirms that billing is managed by the provider, ensuring provider payment details, credit cards, and master invoices are never exposed to the client. ## What's Next? - 👉 **[Usage Controls & Recovery →](/manage/billing/usage-controls-and-recovery)**: Configure budget alerts, storage caps, and service recovery steps. - **[Step Credits, Tokens & Storage](/manage/billing/credits-and-allowances)**: Review how step execution, AI tokens, and storage are metered. - **[Administer a Workspace](/manage/overview)**: Explore complete workspace administration, team management, and governance. --- Source: https://docs.getglow.ai/manage/billing/overview # Billing & Usage Overview > How Glow bills workspace resources, tracks monthly allowances, and separates operational usage from financial management. Glow uses a transparent prepaid resource model where each workspace purchases and tracks monthly allowances in advance. Instead of complex point conversions or surprise overage charges at the end of the month, your team manages three independent resources: **Step credits**, **Glow AI tokens**, and **Retained storage**. > **Role-based visibility:** All workspace members (Admins, Managers, and > Members) can open **Billing & Usage** to monitor live consumption meters and > review published pricing. Financial controls—including payment methods, > invoices, subscription changes, and budget safeguards—are strictly reserved > for **Admins**. ## The three workspace resources Every workflow action in Glow draws from one of three distinct meters: - [Step Credits](/manage/billing/credits-and-allowances#1-step-credits): Standard triggers, app actions, logic gates, and custom code executions. 1 credit per executed step. - [Glow AI Tokens](/manage/billing/credits-and-allowances#2-glow-ai-tokens): Hosted AI prompts, structured data transformation, and autonomous agent loops. Input, output, and reasoning tokens. - [Retained Storage](/manage/billing/credits-and-allowances#3-retained-file-storage): Standing workspace capacity in Gigabytes for uploaded assets, spreadsheets, attachments, and generated files. > **Strict Resource Isolation:** Glow AI tokens do not convert into Step > credits, and Step credits cannot be spent on hosted AI model generation. Each > balance operates independently so an intense AI task never exhausts your > routine operational triggers. --- ## The four billing views Access billing at any time by clicking your workspace name in the top-left sidebar and selecting **Billing & Usage** (or navigating directly to `/app/subscription`). The interface organizes consumption and administration into four dedicated tabs: - [1. Usage Tab](/manage/billing/overview#tab-breakdown-and-permissions): **All Workspace Roles:** Live meter gauges for Step credits, AI tokens, and storage with billing cycle reset dates. - [2. Pricing Tab](/manage/billing/overview#tab-breakdown-and-permissions): **All Workspace Roles:** Interactive calculators to estimate capacity rates and review commercial terms. - [3. Controls Tab](/manage/billing/overview#tab-breakdown-and-permissions): **Admins Only:** Token safeguards, storage hard caps, threshold alerts (80%, 90%, 100%), and webhooks. - [4. Billing Tab](/manage/billing/overview#tab-breakdown-and-permissions): **Admins Only:** Subscription plans, payment methods, billing contacts, and downloadable PDF invoices. ### Tab breakdown and permissions | Tab | Key information and actions | Who can access | | :----------- | :-------------------------------------------------------------------------------------------------------------------- | :------------------------ | | **Usage** | Live meter gauges for Step credits, AI tokens, and storage; current billing cycle dates; upcoming monthly reset date. | Admins, Managers, Members | | **Pricing** | Interactive sliders to calculate rates for additional capacity and review commercial terms. | Admins, Managers, Members | | **Controls** | AI token safeguards, storage limits, custom notification thresholds (80%, 90%, 100%), and alert webhook endpoints. | **Admins only** | | **Billing** | Current plan summary, payment card management, tax/VAT details, invoice history, PDF downloads, and plan adjustments. | **Admins only** | > **Client & Agency Workspaces:** For client workspaces managed under an MSP or > agency sponsorship, the **Usage** tab shows live operational metrics. The > **Billing** tab informs the client that billing is centrally managed by the > agency, keeping agency payment instruments and master invoices completely > private. --- ## How allowances and resets work Glow aligns resource replenishment with your workspace's billing anchor date: - **Step credits**: Refills to full allowance monthly. - **Glow AI tokens**: Refills to full allowance monthly. - **Retained storage**: Standing capacity (never resets). - **Monthly replenishment:** On your monthly reset date, your workspace receives its fresh allowance of Step credits and Glow AI tokens. Unused consumable credits from the previous cycle do not roll over. - **Standing storage:** Retained storage represents active cloud disk capacity and does not reset. Your files remain permanently stored and accessible across billing cycles. - **Annual billing:** Choosing an annual subscription bills twelve months in advance while continuing to grant your full consumable allowance every month on schedule. --- ## Predictable operations without unexpected overages Glow never silently runs into surprise debt or automatically charges unapproved overage fees. 1. **Graceful step pausing:** If your monthly Step credits or AI tokens reach 100%, subsequent steps requiring that specific resource pause with an informative status (`out_of_credits` or `glow_ai_token_limit`). 2. **Unaffected workflows continue:** Workflows that do not require the exhausted resource (for example, non-AI automations when AI tokens run low) continue executing normally. 3. **Trigger preservation:** Inbound webhooks and scheduled triggers remain deployed and active. Once your allowance resets or capacity is adjusted, runs resume automatically. ## What's Next? - 👉 **[Step Credits, Tokens & Storage →](/manage/billing/credits-and-allowances)**: Detailed consumption rules across all step types, loops, and AI models. - **[Purchasing & Changing Amounts](/manage/billing/subscription-plans)**: Select custom resource amounts and manage monthly or annual terms. - **[Usage Controls & Recovery](/manage/billing/usage-controls-and-recovery)**: Set automated safeguards, email alerts, and webhook notifications. --- Source: https://docs.getglow.ai/manage/billing/subscription-plans # Purchasing & Changing Amounts > How to customize your workspace resource amounts, switch between monthly and annual terms, and manage subscription upgrades or cancellations. Glow allows workspace Admins to customize resource capacity to match exact operational requirements. You choose your allowances for **Step credits**, **Glow AI tokens**, and **Retained storage** independently, with no compulsory base platform fees or rigid bundled tiers. --- ## Choosing your monthly amounts To configure or change your workspace plan, navigate to **Billing & Usage → Usage** (or **Pricing**) as a workspace Admin. | Resource Option | Self-Service Range | Metering Basis | | :------------------- | :------------------------ | :---------------------------------- | | **Step Credits** | 1,000 to 100,000+ credits | 1 credit per executed non-AI step | | **Glow AI Tokens** | 1M to 50M+ tokens | Input, output, and reasoning tokens | | **Retained Storage** | 1 GB to 100 GB+ | Standing file volume (GB) | ### Adjust resource sliders Select the required capacity for each resource independently: - **Step credits:** Monthly execution allowance for standard triggers and workflow steps. - **Glow AI tokens:** Monthly token allowance for hosted AI prompts, transforms, and agent reasoning. - **File storage:** Standing workspace storage volume for files, attachments, and datasets. ### Select your billing interval - **Monthly:** Billed on your monthly anchor date, renewing automatically every month. - **Annually:** Billed once for a 12-month period, providing twelve consecutive monthly allowance grants. Annual pricing is calculated as exactly twelve times the monthly rate (no base platform fee). ### Review quote and checkout Select **Continue to checkout** to review the itemized summary before tax, submit your payment details through the secure provider checkout, and return to your refreshed workspace. > **High-Volume & Enterprise Requirements:** If your team requires capacity > beyond the standard self-service ranges, the interface will present a **Talk > to us** option to configure a custom allocation with our solutions team. --- ## How changes take effect (Upgrades vs. Downgrades) As your automation workload evolves, you can adjust your resource amounts at any time: 1. In **Billing & Usage → Billing**, select **Change amounts** (or click **Change amounts** on the **Usage** tab). 2. Move the sliders to your desired new amounts and select **Review changes**. 3. The confirmation modal itemizes the exact financial and timing impact before you confirm. Select a scenario below to see how Glow handles consumable allowances (Step credits & AI tokens) vs. persistent infrastructure (file storage): ### Immediate Capacity Upgrades **When to use:** Your team is running high-volume workflows and needs additional Step credits or Glow AI tokens before the next monthly reset. - **Activation:** **Immediate**. The additional allowance is credited as soon as payment is confirmed. - **Billing calculation:** You are charged only the **prorated difference** for the remaining days in your current billing period. - **Workflow impact:** Paused workflows resume execution immediately. > **Example:** Upgrading from 1,000 to 5,000 Step credits halfway through a monthly billing cycle charges half of the monthly price difference and immediately expands your available balance by 4,000 credits. ### Scheduled Consumable Reductions **When to use:** You want to lower your monthly Step credit or AI token allowance starting with your next billing cycle. - **Activation:** Takes effect at your **next subscription renewal date** (monthly or annual). - **Current term retention:** You retain full access to your existing higher allowance through the end of the paid period. - **Billing calculation:** No mid-cycle refund is issued; your new lower subscription rate automatically applies on your next invoice. > **Example:** Lowering your allowance from 10,000 to 2,500 credits on day 10 of a monthly cycle keeps your 10,000 allowance active until day 30. The 2,500 allowance and lower price take effect on day 1 of the next cycle. ### Real-Time Storage Adjustments **When to use:** You want to expand or reduce the disk storage available for uploaded assets and generated files. - **Activation:** **Immediate** in both directions. - **Increasing storage:** Immediately raises your team upload ceiling. - **Decreasing storage:** If your stored files exceed the new lower storage capacity, existing files remain completely safe, readable, and downloadable. However, new file uploads will pause until space is freed or capacity is expanded. The review dialog requires acknowledging this condition before confirming. --- ## Managing scheduled changes When an allocation decrease or interval adjustment is scheduled for renewal: - The **Billing** overview displays the pending configuration alongside the effective renewal date. - If your requirements change before that date, selecting **Change amounts** and confirming a new configuration will replace the previously scheduled change. --- ## Cancellation and resumption Workspace Admins can cancel an active subscription at any time: ### Open cancellation dialog In **Billing & Usage → Billing**, select **Cancel subscription**. ### Confirm term-end cancellation Review the cancellation summary and select **Cancel at term end**. - Your subscription will not renew at the end of the current paid period. - Your workflows and purchased allowances remain fully active through your displayed period end date. - Existing workspace files, automations, and credentials are kept intact. ### Resuming a subscription If you decide to keep your subscription before the term ends, select **Resume subscription** from the **Billing** tab to clear the scheduled cancellation and maintain continuous service. > **Transition to Free tier:** When a cancelled subscription reaches its term > end date, the workspace automatically transitions to standard Free allowances. > If stored files exceed Free storage capacity, files remain intact, but new > file uploads pause until storage is managed. ## What's Next? - 👉 **[Invoices & Payment Methods →](/manage/billing/invoices-and-payments)**: Update corporate payment cards, download receipts, and manage billing contacts. - **[Usage Controls & Recovery](/manage/billing/usage-controls-and-recovery)**: Configure automated alerts, safeguards, and recovery workflows. - **[Permissions & Access](/manage/workspace-settings/permissions-and-access)**: Review team roles and administrative privileges. --- Source: https://docs.getglow.ai/manage/billing/usage-controls-and-recovery # Usage Controls, Alerts & Recovery > How to configure automated usage safeguards, email and webhook alerts, manage payment grace periods, and restore paused workflows. Glow provides granular operational controls to help engineering leads and financial administrators prevent runaway usage, receive proactive alerts before allowances deplete, and quickly recover workflows if a limit or payment issue occurs. --- ## Managing budgets, caps, and alerts Workspace Admins can configure custom usage thresholds and notification channels from **Billing & Usage → Controls** by selecting **Manage budgets, caps and alerts**. | Setting Field | Example Configuration | Purpose | | :-------------------------- | :------------------------------- | :------------------------------------------------------------------------ | | **AI-token safeguard** | `5,000,000 tokens` | Custom ceiling for hosted Glow AI token consumption in the active period. | | **Retained-storage cap** | `25.0 GB` | Disk volume cap for stored and generated workspace files. | | **Alert thresholds** | `80, 90, 100 %` | Percentage milestones that trigger email and webhook alerts. | | **Additional email alerts** | `ops@company.com` | Extra notification recipients alongside workspace Admins. | | **Billing webhook URL** | `https://api.example.com/alerts` | Webhook destination for signed real-time alert JSON payloads. | > **Immediate Enforcement:** Lowering a safeguard or hard cap below current > usage blocks new applicable operations immediately. Work that has already been > admitted to the execution queue completes normally. ### Available safeguards | Control | Function | What happens when reached | | :---------------------------- | :------------------------------------------------------------------------------------------ | :---------------------------------------------------------------------------------------------- | | **AI-token safeguard** | Sets a custom ceiling on hosted Glow AI token consumption for the current period. | Hosted AI steps pause with `glow_ai_token_limit`. Standard non-AI steps continue executing. | | **Retained-storage cap (GB)** | Restricts the maximum disk volume allocated to uploaded and generated files. | New file uploads and file-creation steps pause. Existing files remain available and accessible. | | **Alert thresholds (%)** | Defines percentage marks (defaults: **80%**, **90%**, **100%**) that trigger notifications. | Dispatches email alerts and webhook payloads as consumption crosses each threshold. | --- ## Notification channels When consumption reaches configured thresholds or an account event occurs, Glow dispatches notifications across two channels: ### 1. Email notifications - **Automatic recipients:** All workspace **Admins** and the verified **billing contact** are always notified. - **Additional recipients:** Admins can specify extra team distribution lists (such as `finops@example.com` or `devops@example.com`) in the controls dialog. ### 2. Billing webhooks You can connect an automated monitoring pipeline by supplying a **Billing webhook URL** (HTTPS): - When a threshold is crossed, Glow posts an HMAC-signed JSON payload containing the workspace identifier, resource kind, threshold reached, and current usage. - Updating or adding a webhook URL automatically generates and displays a unique signing secret for verification. --- ## Understanding pause states When workflow execution stops for a billing-related reason, the cause falls into one of two distinct categories: - [1. Resource Exhaustion](#1-resource-exhaustion): **Allowance Depleted:** Only steps requiring the exhausted balance pause. Recovers on monthly reset or capacity upgrade. - [2. Payment Grace Period](#2-payment-issues--seven-day-grace-period): **Unpaid Renewal Invoice:** Workflows run normally during a 7-day grace window. Resolved immediately via **Pay now**. ### 1. Resource exhaustion - Occurs when monthly Step credits or AI tokens reach 100% of the purchased allowance. - Steps requiring that resource pause with clear status codes (`out_of_credits` or `glow_ai_token_limit`). - Steps that do not require the exhausted resource (for example, standard webhook ingestion when only AI tokens are depleted) continue running. ### 2. Payment issues & seven-day grace period - Occurs if an automated renewal payment fails or requires customer action. - The workspace enters a **seven-day payment grace period**. During grace, workflows continue running normally while alert banners notify Admins to update payment details. - If payment is not completed before grace expires, workflow execution pauses until the outstanding invoice is cleared. --- ## Restoring service and recovering workflows Restoring active service is straightforward and maintains full operational continuity: ### Restore allowance or resolve invoice If an allowance is exhausted, navigate to **Billing & Usage → Usage** and increase your resource amounts, or wait for the scheduled monthly reset date. If an invoice is outstanding, open **Billing & Usage → Billing**, select **Pay now**, and confirm payment of the open balance. ### Service resumes without resetting usage Once payment or capacity expansion is confirmed, active service resumes immediately. The current billing period continues with its recorded history intact; usage counters are never wiped or reset prematurely. ### Verify live workflows Confirm that your workflows display **Live** status in the workflow editor. For webhook-triggered workflows, triggers remain deployed at their URLs during pauses; sending a test payload confirms the restored execution path. For scheduled automations, check the **Next runs** indicator to verify the upcoming execution time. > **Missed events during exhaustion:** Events sent to a paused workflow during > an exhaustion window are not automatically queued or replayed. Verify external > sender logs if historical events need manual reprocessing. ## What's Next? - 👉 **[Billing & Usage Overview →](/manage/billing/overview)**: Review the complete workspace billing architecture. - **[Step Credits, Tokens & Storage](/manage/billing/credits-and-allowances)**: Detailed consumption rules across all step types. - **[Activity & Workflow Monitoring](/manage/workspace-settings/activity)**: Monitor live workflow health, execution logs, and alert queues. --- Source: https://docs.getglow.ai/manage/overview # Administer a Workspace > Manage access, connections, shared data, workflow operations and governance across a Glow workspace. Run a Glow workspace through its full lifecycle: give people the right access, connect the services they need, protect shared values, monitor workflows and keep changes recoverable. - [People and access](/manage/workspace-settings/permissions-and-access): Understand the access model, then invite members and assign workspace roles. - [Apps and connections](/manage/apps-and-integrations/overview): Connect accounts, understand credential ownership and repair connections that need attention. - [Secrets and shared values](/manage/workspace-settings/secrets-and-variables): Keep reusable configuration and sensitive values outside individual Step fields. - [Monitor workflow health](/manage/workspace-settings/activity): Review runs across the workspace and work through alerts in the shared Needs Attention queue. - [Control data and files](/manage/workspace-settings/where-data-lives): See where connections, variables, secrets, files and run data live before you decide how to manage them. - [Security and governance](/manage/workspace-settings/security-compliance): Review access controls, data handling and the settings used to govern the workspace. - [Billing and usage](/manage/billing/overview): Monitor Step credits, AI tokens, and storage capacity, and manage team plan amounts. ## A practical operating order ### Before people build Start with [Permissions and Access](/manage/workspace-settings/permissions-and-access), then use [Team Management](/manage/workspace-settings/team-management) for invitations and role changes. Set workflow visibility separately when only part of the workspace should open a workflow. ### Before a workflow goes Live Confirm who owns each [connection](/manage/apps-and-integrations/managing-connections), keep reusable configuration in [Secrets and Variables](/manage/workspace-settings/secrets-and-variables), and save a version before a risky change. [Operating Workflows](/build/core-concepts/operating-workflows) provides the test-to-Live checklist. ### Once workflows are running Use [Activity](/manage/workspace-settings/activity) for workspace-wide runs and alerts. Open the [Execution Log](/build/the-canvas/execution-log) for the path through one workflow, then inspect [Step-Level Executions](/build/core-concepts/executions) for the input and output of one Step. ### When somebody leaves Remove their workspace access, review workflows and connections they owned, and transfer any operational responsibility before disconnecting an account. Use [Where Data Lives](/manage/workspace-settings/where-data-lives) to identify the workspace resources involved. ## What's Next? - 👉 **[Review Permissions and Access →](/manage/workspace-settings/permissions-and-access)** - Managing allowances and plans? Start with [Billing & Usage](/manage/billing/overview). - Connecting your first service? Start with [Apps & Integrations](/manage/apps-and-integrations/overview). - Investigating a problem? Open [Activity](/manage/workspace-settings/activity). --- Source: https://docs.getglow.ai/manage/workspace-settings/activity # Activity > Alerts that need attention and every run across the workspace, in one place your whole team shares. Activity answers: **What happened across the workspace, and which runs or alerts need attention?** Use it for workspace-wide monitoring and ownership. For the run history of one open workflow, use its [Execution Log](/build/the-canvas/execution-log); open a [Step's Executions tab](/build/core-concepts/executions) only when you need recorded input and output. Open Activity from the sidebar. When alerts are waiting, the number appears beside it, so you can see there is something to deal with without opening the page.  *Two-column Activity layout: execution runs on the left and the shared Needs Attention queue on the right.* ## Two-Column Activity Layout The Activity page presents a unified two-column dashboard: - **Executions (left column):** Recent workflow runs across the workspace, with status, start time, duration and step progress. - **Needs Attention (right column):** Open operational alerts that your team can acknowledge, assign or resolve. ## Smart Alert Counting The red badge beside **Activity** shows how many new alerts have not yet been acknowledged. Acknowledging an alert clears its unread state, but the alert remains **Open** until somebody marks it resolved. ## Alerts & Needs Attention Filter alerts by **Open**, **Resolved** or **All statuses**, then narrow them by **High**, **Medium** or **Low** severity. Open includes both new and acknowledged alerts. | Action | What it does | | :------------------- | :---------------------------------------------------------------------------------------------------------------------------------- | | **Acknowledge** | Records that somebody has reviewed a new alert. It remains in the Open queue. | | **Assign** | Gives the alert to you or another workspace member. Assignment also acknowledges it; choose **No one assigned** to clear ownership. | | **Open in canvas** | Opens the affected workflow when the alert points to a workflow, trigger or scheduler problem. | | **Mark as resolved** | Removes the alert from the Open queue. Filter by **Resolved** to review it later. | Other alert types take you to the setting that needs attention, such as **Connections**, **Team Settings** or subscription management. If the same condition occurs again after resolution, Glow can create or reopen an alert for the new occurrence. ## Executions Table The executions table lists recent runs for workflows you can access. Filter by **All runs**, **Running**, **Failed**, **Completed** or **Canceled**, or search for an exact execution ID or correlation ID. Open a row to see its workflow, timing, step timeline, attempts, error summary and related runs. Use **Open in canvas** when you need the step inputs and outputs. **Download CSV** exports the current page of 25 rows, after applying the active status filter or ID search. It does not download every page of workspace history or the Needs Attention queue. Execution history follows the workspace retention policy, and each run keeps the policy that was active when it started. See [Data Retention](/reference/system-limits#data-retention). ## From a run to one Step Activity owns the whole-run view across the workspace. Open **Open in canvas** for the affected workflow, then select the relevant Step and use its [Executions tab](/build/core-concepts/executions) for Step input, output, and attempt details. ## What's Next? - 👉 **[Step-Level Executions →](/build/core-concepts/executions)**: inspect the input and output of the Step that needs attention. - **[Error Handling & Retries](/build/core-concepts/error-handling)**: configure future retry and error-route behavior. - **[Troubleshooting & Common Errors](/reference/troubleshooting)**: look up a symptom or exact error message. --- Source: https://docs.getglow.ai/manage/workspace-settings/developer-settings # Developer Settings > Trigger workflows from your own systems and read back execution history over the REST API. Glow gives you two programmatic surfaces: **webhook triggers** to start a workflow from your own code, and a **REST API** to read back what happened during a run. ## Triggering a Workflow Workflows are started by sending a request to a webhook trigger's URL. ### Add a Webhook trigger Add a [Webhook](/build/triggers/webhook) trigger to your workflow and switch the workflow to **Live**: the URL exists from that moment. Copy it from the trigger's endpoint field. A test-data run sends nothing over the network and mints no URL. ### Send your payload `POST` your JSON to that URL. ### Add a correlation ID Include a `headers` object in your JSON body, carrying `x-glow-correlation-id` with a value unique to this one request: an order ID, a request ID, or a UUID (a randomly generated unique code). You look the run up by it afterwards. The ID travels inside the body, not as an HTTP header. That `headers` object is stored with the rest of the payload, so it sits alongside your own fields in the trigger's output and reads as `{{ 1.headers }}`. Pick a top-level field name other than `headers` for your own data. When you later **read the results back**, the same name is a real HTTP header on that request — inside the body to start a run, as a header to fetch it. ```bash curl -X POST https://