# The flowmage-flows skill

The flowmage-flows skill in one file: SKILL.md first, then every file it refers to, each under its path.

## File: SKILL.md

---
name: flowmage-flows
description: Writes and edits Flowmage flow files (`<name>.flowmage.json`) that import, publish and run in Flowmage the first time. Use it whenever someone wants a Flowmage flow, automation or workflow built, or hands over a .flowmage.json to change, fix, extend, rewire or upgrade, even when they never say "JSON" or "file".
metadata:
  release: "4"
---

# Flowmage flows

You deliver ONE file, `<name>.flowmage.json`: a Flowmage flow export. The customer imports it, adds
its trigger and publishes it. Import checks only the file's shape; publishing checks every node,
port, config value and expression, and a run checks the data. So every type, key, port and function
you write comes from the pages in this folder, never from memory, and the self-check at the end
replaces the publish you cannot do yourself. Flow file format version 1.

## The file

```json
{
  "format": "flowmage.flow",
  "formatVersion": 1,
  "exportedAt": "2026-01-01T00:00:00.000Z",
  "name": "Save orders",
  "graph": {
    "nodes": [
      { "id": "__start__", "type": "core.start", "version": 1, "label": "Start", "config": {}, "position": { "x": 0, "y": 0 } },
      { "id": "save_order", "type": "table.insert", "version": 1, "label": "Save order",
        "config": { "values": { "email": "{{ trigger.body.email }}", "total": "{{ trigger.body.total }}" }, "tableId": "template:table:orders" },
        "position": { "x": 224, "y": 0 } }
    ],
    "edges": [ { "source": "__start__", "sourcePort": "out", "target": "save_order" } ]
  },
  "requires": {
    "connections": [],
    "tables": [ { "id": "template:table:orders", "name": "Orders", "columns": [
      { "name": "Email", "key": "email", "type": "text" },
      { "name": "Total", "key": "total", "type": "number" } ] } ],
    "schemas": [], "files": [], "secrets": [], "customNodes": [], "flows": []
  }
}
```

`requires` names everything the flow uses from the customer's workspace. Import matches each entry
by name and swaps in the workspace's real id; a value it cannot match is cleared from the step, and
the customer picks it again. Optional top-level keys: `description`, `tags`, `subflow`
(format.md).

## Create

1. Settle the trigger: manual, webhook, schedule, event or watch. A trigger is not a node; the customer
   adds it after import, and steps read its data as `trigger.*` (triggers.md).
2. Pick each step in nodes/INDEX.md, then read its family page. Write the newest version, start
   from the page's example node with every default kept, and replace each `<…>`.
3. Give every node a short unique `id` (`fetch_orders`) and a unique `label` that says what it
   does ("Fetch orders"). Expressions find a step by its label.
4. Wire `{source, sourcePort, target}` edges: Start's `out` to the first step, then each port to
   its next step. Ports are on the node page: `out`, `true`/`false`, `body`/`done`,
   `try`/`success`/`catch`, or one per row `id` (graph-and-wiring.md).
5. Put data in with `{{ }}` expressions (expressions.md, expression-functions.md).
6. Fill `requires` from what the graph uses (format.md, connections-and-secrets.md,
   tables-and-files.md).
7. Position the nodes (layout.md): Start at 0,0, each next step 224 to the right, branches 192
   apart.
8. Run the self-check, write the file, and end your reply with the after-import list.

## Edit

The customer's file is their live flow: change exactly what they asked and copy the rest through
unchanged. Every other node keeps its id, type, version, label, position, config, code, settings
and disabled; every other edge and note stays; so do `name`, `description`, `tags` and `subflow`.
Keep the `requires` entries, ids included, and add or drop only what your change adds or drops.

- Rename a step: change its `label`, then rewrite every `node["Old label"]` that names it.
- Insert between A and B: replace the edge A→B with A→new (A's same port) and new→B; place the new
  node between them.
- Delete a step: drop it and its edges, and reconnect the steps around it when the chain goes on.
- Upgrade a node's version, or fix a flow Flowmage refuses: editing-and-upgrades.md and
  troubleshooting.md.

## Rules that break files most often

1. Only `type@version` pairs from the node pages. Start is `core.start`; triggers have no node type.
2. One Start: id `__start__`, label `Start`, nothing wired into it. Every step is reachable from
   Start; publishing silently drops the rest.
3. Ids are unique; labels are unique.
4. One edge per port, Start's `out` included; to do two things, chain them. Repeat with
   `core.loop`, never with an edge back.
5. A condition is a real boolean: `{{ json.total > 100 }}`, `{{ json.email != null }}`. `==` is
   strict and nothing is truthy.
6. Webhook data is `trigger.body.<field>`, anywhere in the flow.
7. In a loop body the current item is `loop.item`; the body's first step gets `json` = null.
8. A secret is `{{ secret.NAME }}` and is listed in `requires.secrets`; credentials never appear in
   the file.
9. Every literal connection, table and subflow id in a config is declared in `requires` with the
   same id (a new one is a placeholder such as `template:connection:hubspot`), and so is every file
   or schema name a step names.
10. Table values and filters use column keys (`first_name`), and the value has the column's type:
    a whole-value `{{ json.qty }}` or `5` for a number, never `"5"`.
11. Inside a JSON string, name a step with single quotes: `"{{ node['Fetch orders'].body }}"`.
12. Write `settings` and `disabled` only when the customer asks for them.

## Ask or take the default

When the request says what the flow should do, write the file. Choose the details yourself
(labels, a table's columns, a schedule, the timeout path of an approval) and name each choice in
your reply. Ask one question first only when every likely answer gives a different flow: the
request leaves open which system to read or write, or what to send. When you are told to write the
file, there is nobody to ask: take the likeliest reading and say which.

## After import: tell the customer

End your reply with this list, filled in for this file; leave out lines that do not apply.

- Import: Flows → New flow → Import from a file → Import flow. The review shows what is missing.
  An edit goes over its own flow instead: open that flow, its name's menu → Import… → Upload file,
  then Replace canvas after the review. The flow keeps its trigger: leave out the Trigger line.
- Connections: one per `requires.connections` entry, with that exact name and provider. A step
  whose connection was not found shows an empty picker: pick it there.
- Tables: each `requires.tables` entry, with its columns. A missing one shows "Create a table" on
  its step, filled in.
- Secrets (Manage → Secrets), schemas and files: create each by its exact name.
- Subflows: import the called flow first and publish it (Send to Test, and Put on Prod for
  production runs); then import this flow, so its call finds the called flow by name.
- Trigger: Triggers tab → Add trigger → New trigger… (webhook; schedule with cron and time zone;
  workspace event, with what happens and where) or Existing trigger… (a watch, with Added, Updated
  or Removed ticked).
- Publish → Send to Test, try a run, then Put on Prod.

## Self-check: read the file once more; every line must hold

- [ ] It is valid JSON, and its top-level keys are among format, formatVersion, exportedAt, name,
      graph, requires, description, tags, subflow.
- [ ] Every node's type@version is on its family page; every config key is one the page lists, with
      a value of the listed type; no `<…>` is left.
- [ ] One Start; unique ids; unique labels; every node reachable from Start.
- [ ] Every edge leaves a port its source has; at most one edge per port; no cycle.
- [ ] Every `node["X"]` names a step that runs before it on the same path.
- [ ] Every `secret.X`, literal connection/table/flow id, `file("…")` and `schema("…")` is in
      `requires`, and `requires` lists nothing the graph does not use.
- [ ] No two nodes share a position.
- [ ] Edit: compared with the input, every difference is one the customer asked for.

## Pages

| page | read it when |
|---|---|
| references/nodes/INDEX.md, then the family page | adding or changing any step |
| references/format.md | top-level keys, `requires`, tags, what import does, size limits |
| references/graph-and-wiring.md | ports, branches, joins, what publishing checks |
| references/expressions.md | any `{{ }}`, reading earlier steps, null, JavaScript steps |
| references/expression-functions.md | any function in `{{ }}`, and the `trigger` fields |
| references/triggers.md | webhook data and responses, schedules, events, watches, manual runs |
| references/loops-and-errors.md | loops, paging, break/return, try/catch, delays, waits, approvals |
| references/subflows.md | calling another flow, or writing one with parameters |
| references/tables-and-files.md | workspace tables, files, schemas, CSV/JSON/XML |
| references/connections-and-secrets.md | vendor steps, HTTP authentication, secrets, AI and SQL steps |
| references/layout.md | positions and sticky notes |
| references/editing-and-upgrades.md | every edit, version upgrades |
| references/troubleshooting.md | the customer quotes a message or an error code Flowmage showed |
| examples/webhook-save-lead.flowmage.json, examples/schedule-hubspot-to-table.flowmage.json, examples/http-with-alert.flowmage.json (each with a `.md` of the same name) | a webhook with a token check, update-or-insert and a reply; a schedule with a vendor read, a loop and Slack; an HTTP call with authentication, try/catch and an email: complete, working files to copy patterns from |

<!-- source-stamp: graph=fa04a94a91a1 compile=0bb4f9e964c5 expressions=c785b5463c97 transfer=34bdb9a2d3f8 editor=b42484134ac1 -->


---

## File: examples/http-with-alert.flowmage.json

```json
{
  "format": "flowmage.flow",
  "formatVersion": 1,
  "exportedAt": "2026-01-01T00:00:00.000Z",
  "name": "Check open orders",
  "graph": {
    "nodes": [
      {
        "id": "__start__",
        "type": "core.start",
        "version": 1,
        "label": "Start",
        "config": {},
        "position": {
          "x": 0,
          "y": 0
        }
      },
      {
        "id": "guard",
        "type": "core.tryCatch",
        "version": 1,
        "label": "Guard the call",
        "config": {},
        "position": {
          "x": 224,
          "y": 0
        }
      },
      {
        "id": "fetch_orders",
        "type": "core.http",
        "version": 2,
        "label": "Fetch orders",
        "config": {
          "method": "GET",
          "url": "https://api.example.com/v1/orders",
          "query": {
            "status": "open"
          },
          "headers": {},
          "bodyEncoding": "text",
          "responseFormat": "auto",
          "fileName": "",
          "followRedirects": true,
          "auth": {
            "kind": "bearer",
            "headerName": "Authorization",
            "queryName": "access_token",
            "token": "{{ secret.ORDERS_API_TOKEN }}"
          },
          "pagination": {
            "mode": "off",
            "linkFrom": "body",
            "linkPath": "",
            "pageParam": "page",
            "firstPage": 1,
            "offsetParam": "offset",
            "limitParam": "limit",
            "pageSize": 100,
            "cursorPath": "meta.next_cursor",
            "cursorParam": "cursor",
            "itemsPath": "",
            "maxPages": 20
          },
          "connection": ""
        },
        "position": {
          "x": 448,
          "y": -192
        }
      },
      {
        "id": "log_check",
        "type": "table.insert",
        "version": 1,
        "label": "Log check",
        "config": {
          "tableId": "template:table:order-checks",
          "values": {
            "checked_at": "{{ $macro.date.iso() }}",
            "orders": "{{ node['Fetch orders'].body }}"
          }
        },
        "position": {
          "x": 448,
          "y": 0
        }
      },
      {
        "id": "alert",
        "type": "ses.send",
        "version": 2,
        "label": "Email the team",
        "config": {
          "connection": "",
          "to": "ops@example.com",
          "subject": "Order check failed at {{ json.node.label }}",
          "body": "{{ json.error.code }}: {{ json.error.message }}",
          "bodyFormat": "text",
          "from": "",
          "cc": "",
          "bcc": "",
          "replyTo": "",
          "attachments": {}
        },
        "position": {
          "x": 448,
          "y": 192
        }
      }
    ],
    "edges": [
      {
        "source": "__start__",
        "sourcePort": "out",
        "target": "guard"
      },
      {
        "source": "guard",
        "sourcePort": "try",
        "target": "fetch_orders"
      },
      {
        "source": "guard",
        "sourcePort": "success",
        "target": "log_check"
      },
      {
        "source": "guard",
        "sourcePort": "catch",
        "target": "alert"
      }
    ]
  },
  "requires": {
    "connections": [],
    "tables": [
      {
        "id": "template:table:order-checks",
        "name": "Order checks",
        "columns": [
          {
            "name": "Checked at",
            "key": "checked_at",
            "type": "datetime"
          },
          {
            "name": "Orders",
            "key": "orders",
            "type": "json"
          }
        ]
      }
    ],
    "schemas": [],
    "files": [],
    "secrets": [
      "ORDERS_API_TOKEN"
    ],
    "customNodes": [],
    "flows": []
  }
}

```

---

## File: examples/http-with-alert.md

# Check open orders

Request: "Every hour, fetch our open orders from api.example.com with our API token and log them;
if the call fails, email ops@example.com."

Shows: `core.http@2` with bearer authentication from a secret, a try/catch whose `success` path
reads the body step by name and whose `catch` path reads `json.error` and `json.node`, an email
from Flowmage's own address (`ses.send@2` with an empty connection), a `json` table column.

After import: create the secret ORDERS_API_TOKEN; the Order checks table is offered; add a
schedule trigger `0 * * * *`; publish.

<!-- source-stamp: compile=0bb4f9e964c5 transfer=34bdb9a2d3f8 -->


---

## File: examples/schedule-hubspot-to-table.flowmage.json

```json
{
  "format": "flowmage.flow",
  "formatVersion": 1,
  "exportedAt": "2026-01-01T00:00:00.000Z",
  "name": "Copy HubSpot contacts",
  "graph": {
    "nodes": [
      {
        "id": "__start__",
        "type": "core.start",
        "version": 1,
        "label": "Start",
        "config": {},
        "position": {
          "x": 0,
          "y": 0
        }
      },
      {
        "id": "list_contacts",
        "type": "hubspot.listRecords",
        "version": 1,
        "label": "List contacts",
        "config": {
          "connection": "template:connection:hubspot",
          "object": "contacts",
          "customObjectType": "",
          "properties": "email,firstname",
          "associations": "",
          "archived": false,
          "limit": 100,
          "after": "",
          "returnAll": true
        },
        "position": {
          "x": 224,
          "y": 0
        }
      },
      {
        "id": "each_contact",
        "type": "core.loop",
        "version": 3,
        "label": "Each contact",
        "config": {
          "items": "{{ node['List contacts'].records }}",
          "collect": "",
          "allPages": false
        },
        "position": {
          "x": 448,
          "y": 0
        }
      },
      {
        "id": "save_contact",
        "type": "table.insert",
        "version": 1,
        "label": "Save contact",
        "config": {
          "tableId": "template:table:contacts",
          "values": {
            "hubspot_id": "{{ loop.item.id }}",
            "email": "{{ loop.item.properties.email }}",
            "first_name": "{{ loop.item.properties.firstname }}"
          }
        },
        "position": {
          "x": 672,
          "y": -96
        }
      },
      {
        "id": "post_count",
        "type": "slack.sendMessage",
        "version": 1,
        "label": "Post count",
        "config": {
          "connection": "template:connection:slack",
          "channel": "#sales",
          "messageFormat": "text",
          "text": "Copied {{ node['Each contact'].itemCount }} HubSpot contacts.",
          "blocks": "",
          "threadTs": "",
          "replyBroadcast": false,
          "unfurlLinks": true,
          "unfurlMedia": true,
          "linkNames": false
        },
        "position": {
          "x": 672,
          "y": 96
        }
      }
    ],
    "edges": [
      {
        "source": "__start__",
        "sourcePort": "out",
        "target": "list_contacts"
      },
      {
        "source": "list_contacts",
        "sourcePort": "out",
        "target": "each_contact"
      },
      {
        "source": "each_contact",
        "sourcePort": "body",
        "target": "save_contact"
      },
      {
        "source": "each_contact",
        "sourcePort": "done",
        "target": "post_count"
      }
    ]
  },
  "requires": {
    "connections": [
      {
        "id": "template:connection:hubspot",
        "name": "HubSpot",
        "provider": "hubspot"
      },
      {
        "id": "template:connection:slack",
        "name": "Slack",
        "provider": "slack"
      }
    ],
    "tables": [
      {
        "id": "template:table:contacts",
        "name": "HubSpot contacts",
        "columns": [
          {
            "name": "HubSpot id",
            "key": "hubspot_id",
            "type": "text",
            "required": true
          },
          {
            "name": "Email",
            "key": "email",
            "type": "text"
          },
          {
            "name": "First name",
            "key": "first_name",
            "type": "text"
          }
        ]
      }
    ],
    "schemas": [],
    "files": [],
    "secrets": [],
    "customNodes": [],
    "flows": []
  }
}

```

---

## File: examples/schedule-hubspot-to-table.md

# Copy HubSpot contacts

Request: "Every morning, copy our HubSpot contacts into a table and post the count in #sales on
Slack."

Shows: two vendor connections with their providers, a list step fetching every page, a loop whose
body writes one row per record (`loop.item`) and whose `done` port reads the loop's `itemCount`,
the loop's two ports laid out above and below.

After import: connections named "HubSpot" (provider HubSpot) and "Slack"; the HubSpot contacts
table is offered with its columns; add a schedule trigger, e.g. `0 7 * * *` in the workspace's
time zone; publish. Each run adds every contact again: to keep one row per contact, look the
contact up first (the insert-or-update recipe in tables-and-files.md).

<!-- source-stamp: compile=0bb4f9e964c5 transfer=34bdb9a2d3f8 -->


---

## File: examples/webhook-save-lead.flowmage.json

```json
{
  "format": "flowmage.flow",
  "formatVersion": 1,
  "exportedAt": "2026-01-01T00:00:00.000Z",
  "name": "Save leads from the website",
  "graph": {
    "nodes": [
      {
        "id": "__start__",
        "type": "core.start",
        "version": 1,
        "label": "Start",
        "config": {},
        "position": {
          "x": 0,
          "y": 0
        }
      },
      {
        "id": "check_token",
        "type": "core.condition",
        "version": 1,
        "label": "Token valid?",
        "config": {
          "expression": "{{ secret.WEBHOOK_TOKEN == trigger.headers['x-token'] }}"
        },
        "position": {
          "x": 224,
          "y": 0
        }
      },
      {
        "id": "find_lead",
        "type": "table.select",
        "version": 1,
        "label": "Find lead",
        "config": {
          "tableId": "template:table:leads",
          "filters": [
            {
              "column": "email",
              "operator": "equals",
              "value": "{{ trigger.body.email ?? \"\" }}"
            }
          ],
          "sort": [],
          "search": "",
          "maxRows": 1,
          "ifMoreRows": "first",
          "cursor": ""
        },
        "position": {
          "x": 448,
          "y": -96
        }
      },
      {
        "id": "reject",
        "type": "core.raiseError",
        "version": 1,
        "label": "Reject call",
        "config": {
          "code": "UNAUTHORIZED",
          "message": "The x-token header is missing or wrong."
        },
        "position": {
          "x": 448,
          "y": 96
        }
      },
      {
        "id": "lead_exists",
        "type": "core.condition",
        "version": 1,
        "label": "Lead exists?",
        "config": {
          "expression": "{{ node['Find lead'].filteredCount > 0 }}"
        },
        "position": {
          "x": 672,
          "y": -96
        }
      },
      {
        "id": "update_lead",
        "type": "table.update",
        "version": 1,
        "label": "Update lead",
        "config": {
          "tableId": "template:table:leads",
          "rowId": "{{ node['Find lead'].rows[0].rowId }}",
          "values": {
            "first_name": "{{ trigger.body.firstName }}",
            "last_seen": "{{ trigger.receivedAt }}"
          }
        },
        "position": {
          "x": 896,
          "y": -192
        }
      },
      {
        "id": "insert_lead",
        "type": "table.insert",
        "version": 1,
        "label": "Insert lead",
        "config": {
          "tableId": "template:table:leads",
          "values": {
            "email": "{{ trigger.body.email }}",
            "first_name": "{{ trigger.body.firstName }}",
            "last_seen": "{{ trigger.receivedAt }}"
          }
        },
        "position": {
          "x": 896,
          "y": 0
        }
      },
      {
        "id": "reply",
        "type": "core.return",
        "version": 1,
        "label": "Reply",
        "config": {
          "value": "{{ parseJson('{\"ok\": true}') }}"
        },
        "position": {
          "x": 1120,
          "y": -96
        }
      }
    ],
    "edges": [
      {
        "source": "__start__",
        "sourcePort": "out",
        "target": "check_token"
      },
      {
        "source": "check_token",
        "sourcePort": "true",
        "target": "find_lead"
      },
      {
        "source": "check_token",
        "sourcePort": "false",
        "target": "reject"
      },
      {
        "source": "find_lead",
        "sourcePort": "out",
        "target": "lead_exists"
      },
      {
        "source": "lead_exists",
        "sourcePort": "true",
        "target": "update_lead"
      },
      {
        "source": "lead_exists",
        "sourcePort": "false",
        "target": "insert_lead"
      },
      {
        "source": "update_lead",
        "sourcePort": "out",
        "target": "reply"
      },
      {
        "source": "insert_lead",
        "sourcePort": "out",
        "target": "reply"
      }
    ],
    "notes": [
      {
        "id": "start-here",
        "text": "Start here: create the secret WEBHOOK_TOKEN, add a webhook trigger set to Sync, and give the caller the URL and the token (header x-token).",
        "position": {
          "x": 0,
          "y": 320
        },
        "size": {
          "width": 240,
          "height": 160
        }
      }
    ]
  },
  "requires": {
    "connections": [],
    "tables": [
      {
        "id": "template:table:leads",
        "name": "Leads",
        "columns": [
          {
            "name": "Email",
            "key": "email",
            "type": "text",
            "required": true,
            "unique": true
          },
          {
            "name": "First name",
            "key": "first_name",
            "type": "text"
          },
          {
            "name": "Last seen",
            "key": "last_seen",
            "type": "datetime"
          }
        ]
      }
    ],
    "schemas": [],
    "files": [],
    "secrets": [
      "WEBHOOK_TOKEN"
    ],
    "customNodes": [],
    "flows": []
  }
}

```

---

## File: examples/webhook-save-lead.md

# Save leads from the website

Request: "Our website posts each sign-up (email, firstName) to a webhook with a token in the
x-token header. Keep one row per email in our Leads table and answer the caller with ok."

Shows: a shared-token check on a webhook, insert-or-update on a table (select by key column,
branch on `filteredCount`), two branches joining into one `core.return` that answers a Sync
webhook, a secret, a table declared with columns, a branch laid out above and below, a note.

After import: create the secret WEBHOOK_TOKEN; the Leads table is offered with its columns; add a
webhook trigger set to Sync; Publish → Send to Test, then Put on Prod.

<!-- source-stamp: compile=0bb4f9e964c5 transfer=34bdb9a2d3f8 -->


---

## File: references/connections-and-secrets.md

# Connections, secrets, HTTP, SQL and AI steps

## Connections

A vendor step (HubSpot, AFAS, Slack, …) signs in through a **connection** the customer keeps in
Flowmage. The file never holds credentials; it names the connection:

```json
{ "id": "create_contact", "type": "hubspot.createRecord", "version": 1, "label": "Create contact",
  "config": { "connection": "template:connection:hubspot", "object": "contacts",
              "properties": { "email": "{{ trigger.body.email }}" } },
  "position": { "x": 224, "y": 0 } }
```

with, in `requires`:

```json
"connections": [ { "id": "template:connection:hubspot", "name": "HubSpot", "provider": "hubspot" } ]
```

- `connection` is a key of `config`, always literal text, never `{{ }}`. A new one is a placeholder
  id (`template:connection:<vendor>`); steps that use the same connection share one id.
- Import finds the customer's connection whose name is exactly `name` **and** whose provider is
  `provider`, and puts its id in every step that holds the placeholder. With no match it clears the
  step's connection and the customer picks one. Use the connection name the customer gave; else the
  product's name ("HubSpot", "AFAS Profit") and say so in your reply.
- In an edit, the ids are already the customer's real ids: keep them and their `requires` entries.
- Every vendor step needs a connection. Only `ses.send@2` and `core.http@2` take `""` (none).
- A connection starts nothing: a flow that should start on a service's events needs a trigger
  (triggers.md, Service triggers). Deleting a connection deletes the triggers that sign in with it.

Provider keys are the type's prefix (`slack.sendMessage` → `slack`), except:

| type prefix | provider |
|---|---|
| afas | `afas-profit` |
| claude | `anthropic` |
| gemini | `google-ai-studio` |
| openai | `openai` |
| mandrill | `mailchimp-transactional` |
| graph | `microsoft-graph` |
| exact | `exact-online` |
| ses | `aws-ses` |
| personio, for createApplication and uploadApplicationDocument | `personio-recruiting` |

## Secrets

A secret is a value the customer stores under Manage → Secrets (an API key, a shared token).

- Write `{{ secret.NAME }}` and list `NAME` in `requires.secrets` (a list of names). Names are the
  customer's; when none was given, pick an UPPER_SNAKE name and tell them to create it. A secret's value is at least 8 characters.
- The secret is the whole `{{ }}`, or one side of `==` / `!=`. Text around the braces is fine:
  `"Bearer {{ secret.API_TOKEN }}"`. Never inside other code: `{{ "Bearer " + secret.API_TOKEN }}`,
  `{{ upper(secret.X) }}` are refused.
- Never in an AI prompt, a webhook response expression, a loop's `collect`, or a JavaScript step.
- Credentials never appear in the file as literals. Literal credentials an exported file had are
  blanked and listed under `requires.secretFields`; tell the customer to fill those fields in.

## HTTP requests (`core.http@2`)

For any API without its own step. `url` is required; `method` `GET` (default), `POST`, `PUT`,
`PATCH`, `DELETE`. `query` is an object of text values; `body` may be an object (sent as JSON) or
text.

Authentication goes in `auth`, never in `headers`:

| API wants | `auth` |
|---|---|
| nothing | `{ "kind": "none" }` |
| a bearer token | `{ "kind": "bearer", "token": "{{ secret.API_TOKEN }}" }` |
| user and password | `{ "kind": "basic", "username": "{{ secret.API_USER }}", "password": "{{ secret.API_PASSWORD }}" }` |
| a key in a header | `{ "kind": "header", "headerName": "X-Api-Key", "token": "{{ secret.API_KEY }}" }` |
| a key in the query | `{ "kind": "query", "queryName": "api_key", "token": "{{ secret.API_KEY }}" }` |
| a Flowmage connection | `{ "kind": "connection" }` plus `"connection": "template:connection:stripe"` in `config` |

- Connection auth takes any provider's connection; its `requires.connections` entry still names
  the real provider. Stripe, Mistral, Recruitee careers and generic OAuth2 client-credentials
  (`oauth2-client-credentials`) connections are used only this way.
- Output: `{ status, headers, body, bodySizeBytes }`; read `node['Fetch orders'].body.items`.
  Header names are lower case.
- A status of 400 or more fails the step as `HTTP_<status>`; 429 and 5xx are retried first. To
  branch on a 404 instead: loops-and-errors.md (carrying on after one failing step).
- `pagination.mode`: `off` (default), `nextLink`, `pageNumber`, `offsetLimit`, `cursor`; up to
  `maxPages` (20, at most 100). The options and defaults are on the core node page. With
  pagination on, `body` is the items of every page as one list.
- `responseFormat: "file"` (with `fileName`) keeps a download as a workspace file.

## Fixed IP

When a system only accepts known IP addresses, set `"settings": { "useFixedIp": true }` on that step
(beside `config`, not inside). A connection has its own "Use fixed IP address" switch, which the
customer sets in Flowmage. SQL steps always use the fixed addresses.

## SQL (`postgres.query`, `mysql.query`, `mssql.query`)

```json
"config": { "connection": "template:connection:postgres", "operation": "select",
            "query": "select id, email from customers where id = $1",
            "params": [ "{{ trigger.body.id }}" ], "maxRows": 1000, "ifMoreRows": "fail" }
```

- `query` takes no `{{ }}`: values go in `params`, in order. Placeholders: Postgres `$1, $2`,
  MySQL `?`, SQL Server `@p1, @p2`.
- `operation`: `select` reads (a write statement there fails), `execute` changes data.
- Output: `{ rows: [{ <column>: value }], rowCount, columns, truncated }`; read
  `node['Find customer'].rows[0].email`.
- The connection must be for that database engine.

## AI steps (`claude.*`, `openai.*`, `gemini.*`: generate, extract, classify)

They run on the customer's own AI connection, and the organisation must have AI switched on.

- `model` is a model id the customer's connection offers (e.g. from their AI provider's model
  list); when unknown, write the provider's current general model id and tell the customer to check
  it in the step. `reasoning`: `default`, `low`, `medium`, `high`.
- generate: `prompt` (no secrets). `output: "text"` gives `{ text }`; `output: "fields"` needs at
  least one field and gives an object with those fields.
- extract: `source: "text"` with `text`, or `source: "file"` with a workspace file name; at least
  one field (`{ name, description, type, required }`, type `text`, `number`, `boolean`, `date`,
  `text-list`, `number-list`). Output is the fields at the top: `node['Read invoice'].invoiceNumber`.
- classify: `text` and at least 2 `labels` `{ id, label, description }` with unique ids, none named
  `other`. Each label id is an output port, plus `other`: wire one edge per port you need.
  Output: the input plus `label`, `confidence`, `reason`.

## Email (`ses.send@2`)

`"connection": ""` sends from Flowmage's own address; `to` holds email addresses (or a `{{ }}`).
With the customer's own mail connection, `from` is required too.

<!-- source-stamp: connections=179f1bf6583b transfer=34bdb9a2d3f8 -->


---

## File: references/editing-and-upgrades.md

# Editing a flow file

The input file is the customer's live flow, exported from Flowmage. The output is the same file with
exactly the asked change: the customer opens that flow and imports the file over it (the flow name's
menu → Import…). Before anything is replaced, the review lists the steps removed, added and changed,
pairing each step with its old self by `id`: a step whose id changed reads as removed and added. The
flow keeps its name, description, tags, subflow parameters, triggers and versions.

## What stays

Copy each of these through unchanged unless the request is about it:

- every node's `id`, `type`, `version`, `label`, `position`, `config`, `code`, `settings`,
  `disabled`;
- every edge (with its `route`) and every note;
- `name`, `description`, `tags`, `subflow`;
- every `requires` entry with its `id`: these ids are the customer's real connection, table and
  flow ids. `requires.secretFields`, when present, stays as it is.

Key order and `exportedAt` do not matter. Work on a copy of the input and change only the parts the
request names; rewriting the whole file from memory loses details.

## Common edits

- **Rename a step**: change its `label`, then rewrite every reference to the old label in every
  config and `code`: `node['Old']`, `node["Old"]` and `node.Old` all become `node['New']`. Flowmage
  does not do this for you on import.
- **Insert a step between A and B**: replace the edge A→B by A→new on the same `sourcePort`, and
  new→B on the new step's port; place it between them (layout.md). If B read A's output as `json`,
  it now gets the new step's output: give B `node['A']` instead where it needs A's data.
- **Delete a step**: drop it, its edges and its `requires` entries no other step uses; connect its
  predecessor's port to its successor; rewrite references to it in later steps.
- **Rewire**: change only the edges named; every port keeps at most one edge.
- **Add a branch**: a condition's `true`/`false`, a switch's case ids (each case `id` is its port
  name). Renaming a case `id` means renaming the `sourcePort` of its edge too.
- **Change a value**: set that one config key; keep the rest of the config as it is.
- **Turn a step off**: `"disabled": true` (the step passes its input through). Only when asked.
- **New step**: start from its node page example with every default kept.

## Upgrading a node version

An older version is listed under its node with "still runs; upgrade it", followed by the
upgrade: set `version` to the newest and convert `config` exactly as the before/after shows, moving
each value the customer had into its new place. Keep the node's `id`, `label`, `position` and edges.
When the older version lists other ports than the newest, rename the `sourcePort` of the edges
that leave it.

Upgrade only the nodes the customer asks about ("upgrade everything" means every node with a newer
version). After the upgrade, the references, `requires` and wiring must still hold: run the
self-check.

## Fixing a flow Flowmage refuses

Find the message or code in troubleshooting.md and fix the cause it names. When the customer gives
no message, run the self-check over the whole file: every rule broken is a fix, and the fixes are
the only changes.

<!-- source-stamp: graph=fa04a94a91a1 transfer=34bdb9a2d3f8 editor=b42484134ac1 -->


---

## File: references/expression-functions.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Expression functions

Every function the `{{ }}` language knows. Names are case-sensitive; there are no others. A function fails on a null argument unless noted; guard with `??` or `default()`.

## convert

- `default(x: any (null ok), fallback: any (null ok))` → any. The value, or the fallback when the value is null — the same as ?? written as a call. Example: `{{ default(null, "unknown") }}`
- `parseJson(s: string)` → any. Reads JSON text into a value you can take fields from. Example: `{{ parseJson('{"id": 7}').id }}`
- `toJson(x: any (null ok))` → string. Turns any value into compact JSON text. Example: `{{ toJson(split("a,b", ",")) }}`
- `toNumber(x: any)` → number. Turns text that is exactly one number into that number. Example: `{{ toNumber("42") }}`
- `toString(x: any (null ok))` → string. Turns any value into text — null becomes empty text. Example: `{{ toString(42) }}`

## date

- `addDays(date: any, days: number)` → any. Moves a date by a number of days — negative goes back. ISO in, ISO out; milliseconds in, milliseconds out. Example: `{{ addDays("2026-07-21T09:00:00Z", 3) }}`
- `daysBetween(from: any, to: any)` → number. The whole days from the first date to the second — negative when the second comes first; a part day does not count. Example: `{{ daysBetween("2026-07-21", "2026-08-04") }}`
- `formatDate(date: any, pattern: string, timezone?: string)` → string. Formats a date (ISO text or milliseconds) with a pattern of yyyy MM dd HH mm ss SSS, in the workspace time zone unless you name one. Example: `{{ formatDate("2026-07-21T09:00:00Z", "dd-MM-yyyy HH:mm", "UTC") }}`
- `subtractDays(date: any, days: number)` → any. Moves a date back by a number of days — addDays the other way. ISO in, ISO out; milliseconds in, milliseconds out. Example: `{{ subtractDays("2026-07-21T09:00:00Z", 7) }}`

## encoding

- `base64Decode(s: string)` → string. Decodes base64 back to text. Example: `{{ base64Decode("dXNlcjpwYXNz") }}`
- `base64Encode(s: string)` → string. Encodes text as base64 (UTF-8) — the Basic-auth header form. Example: `{{ base64Encode("user:pass") }}`
- `urlDecode(s: string)` → string. Turns percent-encoded URL text back into plain text. Example: `{{ urlDecode("Anna%20%26%20Co") }}`
- `urlEncode(s: string)` → string. Makes text safe to put in a URL or a query string. Example: `{{ urlEncode("Anna & Co") }}`
- `xmlEscape(s: string)` → string. Makes text safe to put inside an XML document — & < > and both quotes become entities. Example: `{{ xmlEscape("Anna & Co <BV>") }}`

## list

- `first(list: array)` → any. The first item of a list — null when the list is empty. Example: `{{ first(split("a,b,c", ",")) }}`
- `join(list: array, sep: string)` → string. Joins the items of a list into one text, with a separator between them. Example: `{{ join(split("a,b,c", ","), " · ") }}`
- `last(list: array)` → any. The last item of a list — null when the list is empty. Example: `{{ last(split("a,b,c", ",")) }}`
- `length(x: any)` → number. Counts the items in a list, or the characters in text. Example: `{{ length(split("a,b,c", ",")) }}`
- `unique(list: array)` → array. The list with every duplicate removed. Example: `{{ unique(split("a,b,a", ",")) }}`

## number

- `abs(n: number)` → number. The number without its minus sign. Example: `{{ abs(-12.5) }}`
- `ceil(n: number)` → number. Rounds a number up to a whole number. Example: `{{ ceil(4.2) }}`
- `floor(n: number)` → number. Rounds a number down to a whole number. Example: `{{ floor(4.7) }}`
- `max(a: number, b: number)` → number. The larger of two numbers. Example: `{{ max(3, 10) }}`
- `min(a: number, b: number)` → number. The smaller of two numbers. Example: `{{ min(3, 10) }}`
- `round(n: number, decimals?: number)` → number. Rounds a number to a whole number, or to the decimals you ask for. Example: `{{ round(3.14159, 2) }}`

## service

- `file(name: string)` → object. Reads a workspace file — take .text, .base64 or .size from it. Example: `{{ file("template.html").text }}`
- `schema(name: string)` → any. Reads a workspace schema document, as it was when the run started. Example: `{{ schema("Settings") }}`

## text

- `contains(s: string, sub: string)` → boolean. True when the text contains the piece. Example: `{{ contains("anna@acme.nl", "@acme") }}`
- `endsWith(s: string, suffix: string)` → boolean. True when the text ends with the suffix. Example: `{{ endsWith("report.pdf", ".pdf") }}`
- `lower(s: string)` → string. Converts text to lowercase. Example: `{{ lower("ANNA@ACME.NL") }}`
- `replace(s: string, search: string, replacement: string)` → string. Replaces every occurrence of a piece of text — a literal match, not a pattern. Example: `{{ replace("2026-07-21", "-", "/") }}`
- `split(s: string, sep: string)` → array. Splits text into a list at every separator. Example: `{{ split("a,b,c", ",") }}`
- `startsWith(s: string, prefix: string)` → boolean. True when the text starts with the prefix. Example: `{{ startsWith("INV-2026-081", "INV-") }}`
- `trim(s: string)` → string. Removes the spaces at the start and the end of text. Example: `{{ trim("  Anna  ") }}`
- `upper(s: string)` → string. Converts text to UPPERCASE. Example: `{{ upper("Anna") }}`

## `trigger` fields per trigger kind

- webhook: `trigger.body` any, `trigger.headers` object, `trigger.query` object, `trigger.method` string, `trigger.receivedAt` string, `trigger.file` object, `trigger.file.name` string, `trigger.file.contentType` string, `trigger.file.size` number, `trigger.file.sha256` string
- schedule: `trigger.scheduledAt` string, `trigger.scheduleId` string
- event: `trigger.type` string, `trigger.occurredAt` string, `trigger.subject` object, `trigger.subject.kind` string, `trigger.subject.id` string, `trigger.event` object, `trigger.event.id` string
- watch: `trigger.item` object, `trigger.action` string, `trigger.key` string, `trigger.previous` object, `trigger.changed` array, `trigger.diff` object, `trigger.reason` string, `trigger.list` object, `trigger.list.service` string, `trigger.list.name` string, `trigger.detectedAt` string, `trigger.modifiedAt` string
- manual: `trigger` is whatever the run was started with.


---

## File: references/expressions.md

# Expressions

Data moves through a flow in `{{ }}` expressions inside config strings. Every string in a config
(nested objects and arrays included) is read as a template, so a stray `{{` anywhere is an
expression. Functions: expression-functions.md.

## In the JSON file

```json
"config": { "expression": "{{ default(node['Check the site'].status, 0) != 200 }}" }
"config": { "subject": "Order {{ trigger.body.orderId }} received", "amount": "{{ json.total }}" }
```

- Name steps with single quotes (`node['Fetch orders']`), or escape double quotes (`node[\"Fetch orders\"]`).
- A literal `{{` in text is written `\\{{` in the file (the text `\{{`).
- Numbers, booleans and `null` that are not computed go in as JSON values (`"maxRows": 50`), not as text.
- At most 8192 bytes per field, nesting depth 32, 1000 evaluation steps per field.

## Whole value or text

- **Whole value**: the field is exactly one `{{ }}` (spaces around it are fine). The result keeps its
  type: number, boolean, list, object or null. Use this for every number, boolean, list or object
  field: `"total": "{{ json.total }}"`, `"items": "{{ node['Read rows'].rows }}"`.
- **Text**: anything else (text around it, or two `{{ }}`). Every part becomes text: null → empty
  text, a list or object → compact JSON. `"Hello {{ json.name }}"`.
- A text field refuses a whole-value number: write `"{{ toString(json.id) }}"` or `"#{{ json.id }}"`.
- A number field takes a number, or text that is exactly a number (`"42"`, `"-3.5"`).
- A boolean field takes only `true`/`false`, never the text `"true"`.
- A list or object field needs a whole value that yields a list or object.
- **Null into any typed field fails the run**, even when the field is optional. Guard every value
  that may be missing: `{{ json.phone ?? "" }}`, `{{ default(json.qty, 0) }}`.

## Names (the only roots)

| root | value |
|---|---|
| `json` | this step's input (next section) |
| `node['Label']` | the output of an earlier step, by label or node id; null if it did not run |
| `trigger` | the trigger's data (triggers.md); fields per kind in expression-functions.md |
| `loop` | in a loop body: `item`, `index` (from 0), `first`, `last`, `count`, `parent` (outer loop or null) |
| `params` | a subflow's declared parameters (subflows.md) |
| `secret.NAME` | a workspace secret (below) |
| `execution` | `id`, `environment` (`"test"`/`"prod"`), `mode` (`"version"`/`"draft"`), `resumeUrl` |
| `flow` | `id`, `version` (null in a draft run), `name` |
| `workspace` | `id`, `name`, `timezone` |
| `$macro` | `$macro.random.uuid()`, `$macro.date.iso()` (now, ISO text), `$macro.date.timestamp()` (now, ms); empty parentheses; fixed for the whole run |

Use dots for plain names and brackets for anything else: `node['Send mail']`, `json['x-id']`,
`json.items[0]`. Reading a missing field never fails; it gives null (`json.a.b.c`).

## What `json` is

| where the step sits | `json` |
|---|---|
| first step after Start | the trigger's data (a webhook step reads `json.body.x`; `trigger.body.x` works everywhere) |
| after one step | that step's output |
| after a condition or switch | the condition's own input, passed through |
| where two or more edges join | null: read `node['…']` |
| first step of a loop body | null: read `loop.item` |
| after a loop (`done`) | `{ itemCount, completedCount, collected? }` |
| first step of a try (`try`) | the try/catch's input |
| on `catch` | `{ error: { code, message, details? }, node: { id, label, iteration? } }` |
| on `success` | null: read the try steps by name |

`node['X']` must name a step that runs before this one on the same path: not a step on another
branch, not a later step, not a disabled step, not Start. In a loop body, body steps give this
round's output, and the loop itself cannot be named; after the loop, body steps give the last
round's output. Renaming a step does not rewrite references: change every `node['Old']` yourself.

## Operators

`?:` `??` `||` `&&` `==` `!=` `<` `<=` `>` `>=` `+` `-` `*` `/` `%` `!` and parentheses.

- `==` and `!=` are exact: `"1" == 1` is false; lists and objects compare by content.
- Nothing is truthy. `!`, `&&`, `||` and the `?:` test need real booleans. Test presence with
  `json.x != null`, never `!json.x` or `json.x && …`.
- `??` replaces only null, not `""`, `0` or `false`.
- `+` adds two numbers or joins two texts; mixed fails. Build text by writing it around the
  expression: `"Order {{ json.id }}"`, or `toString()`.
- `<` `>` compare two numbers or two texts (ISO dates compare correctly as text).
- Dividing by zero fails. Decimals are inexact: `round(json.price * 1.21, 2)`.
- No `.length` (gives null): use `length(x)`.

## Not in the language

No list or object literals (`[1,2]`, `{a:1}`; use `parseJson('[1,2]')`), no `?.` (reads are already
null-safe), no `===`, no single `=`, no pipes, no comments, no lambdas or `map`/`filter`, no regex,
no methods on values (`"a".toUpperCase()`), no backtick templates, no exponents (`1e3`). Only the
functions in expression-functions.md, spelled in that case.

## Secrets

- A secret is a whole `{{ secret.NAME }}` part: `"Bearer {{ secret.API_TOKEN }}"` is fine.
- Or one side of `==` / `!=`: `{{ secret.WEBHOOK_TOKEN == trigger.headers['x-token'] }}`.
- Everything else is refused: `secret.A ?? ""`, `secret.A + "x"`, `upper(secret.A)`,
  `base64Encode(json.user + ":" + secret.B)`, `secret.A.field`, a secret inside `?:`.
- List every name in `requires.secrets`. A secret is never in a loop's `collect`, a webhook
  response, or a JavaScript step.

## Code fields

SQL queries, GraphQL documents and other code-editor fields take no `{{ }}`: pass values through
the node's parameters or variables field (connections-and-secrets.md). A `core.javascript` step
keeps its code in the node's own `code` key, not in `config` (`config` is `{}`):

```json
{ "id": "score", "type": "core.javascript", "version": 1, "label": "Score lead", "config": {},
  "code": "const total = (json.orders || []).length;\nreturn { score: total * 10 };",
  "position": { "x": 448, "y": 0 } }
```

The code is the body of `async function run(input)` and ends with `return`. It can read `input`
(= `json`), `trigger`, `loop`, `execution`, `flow`, `workspace`, `params`, `$now`, and
`node['Label']` for earlier steps named literally. No secrets, no network, no `{{ }}`; 4 MiB in and
out, 10 seconds.

<!-- source-stamp: expressions=c785b5463c97 runtime=ebb2f787556b -->


---

## File: references/format.md

# The flow file

A `.flowmage.json` file is one JSON object: the flow's graph plus the list of workspace things it
uses. Import reads only the keys below; any other key is dropped without a word.

## Top-level keys

| key | needed | value |
|---|---|---|
| `format` | yes | exactly `"flowmage.flow"` |
| `formatVersion` | yes | the number `1` (not the string `"1"`) |
| `exportedAt` | yes | an ISO 8601 time, e.g. `"2026-01-01T00:00:00.000Z"` |
| `name` | yes | 1–120 characters after trimming |
| `graph` | yes | `{ nodes, edges, notes? }` (graph-and-wiring.md, layout.md) |
| `requires` | yes | what the flow uses from the workspace (below) |
| `description` | no | up to 500 characters |
| `tags` | no | up to 5 `{ "name": 1–15 characters, "color": one of indigo, violet, fuchsia, rose, amber, emerald, teal, sky, slate }` |
| `subflow` | no | `{ "params": [...] }`: the inputs a caller passes (subflows.md) |

Leave optional keys out rather than empty: no `"tags": []`, no `"notes": []`, no empty
`description`. Triggers, test data, pinned outputs, run settings and the flow's icon are not part of
the file; the customer sets them in Flowmage.

## requires

Every list is present, even when empty, except `flows`, which may be left out when nothing is
called (writing `[]` is fine).

| list | entry | write one for |
|---|---|---|
| `connections` | `{ "id", "name", "provider" }` | each distinct literal top-level `config.connection` |
| `tables` | `{ "id", "name", "columns"? }` | each distinct literal top-level `config.tableId` |
| `flows` | `{ "id", "name" }` | each `core.callFlow` `config.flowId` |
| `secrets` | `"NAME"` | each `{{ secret.NAME }}` anywhere in a config |
| `schemas` | `"name"` | each `schema("name")` in an expression, and `config.name` of a `schema.*` step |
| `files` | `"name"` | each `file("name")` in an expression, and `config.name` of a `file.*` step |
| `customNodes` | `"custom.…"` | each `custom.*` node type (the workspace's own nodes) |

Each list holds exactly what the graph uses: no entry the graph does not use, no use without an
entry. Sort the string lists alphabetically.

An exported file may also hold `requires.secretFields` (`[{ "label", "where" }]`): credentials
typed into a field, which the export blanked. Keep it in an edit; never write it in a new file.

- `id` is the value the step's config holds. In a new flow it is a placeholder no workspace has:
  `template:connection:<vendor>`, `template:table:<slug>`, `template:flow:<slug>`. In an edited
  flow keep the ids already there.
- `name` is the display name as it is, or will be, in the customer's workspace.
- `provider` is the provider key from the node page (`requires.connections` note on the
  connection field), e.g. `hubspot`, `afas-profit`, `anthropic` (connections-and-secrets.md).
- `columns` lists the table's columns: `{ "name", "key", "type", "required"?, "unique"?, "default"? }`,
  type one of `text`, `number`, `boolean`, `datetime`, `json`; `key` matches `^[a-z][a-z0-9_]*$`.
  Write `columns` for every table the flow writes to (tables-and-files.md).

## What import does

1. Checks the shape: the keys above, node and edge fields, size and nesting. A failure refuses
   the file with a message (when the customer quotes it: troubleshooting.md).
2. Matches `requires` against the workspace and swaps ids in the steps:
   - a connection matches on exact name AND provider;
   - a table matches on name, ignoring case;
   - a called flow matches on the exact name of an existing flow.
3. A step whose id did not match loses that key. A table step whose table entry has `columns`
   gets a filled-in "Create a table" instead. An id the file never declared in `requires` is
   removed too, with no line in the review.
4. Secrets, schemas, files and custom nodes are only reported as found or missing; the names stay
   in the steps.
5. The flow lands as a draft with no trigger. A name already in use becomes `Name (2)`.

Import… in an open flow (the flow name's menu in the editor) does steps 1–4 the same way, shows the
steps removed, added and changed, and then replaces only that flow's steps, edges and notes, as one
undo step. The flow keeps its own name, description, tags, subflow parameters, triggers and
versions; the file's are not used.

Ids, labels, positions, configs, edges and notes come through exactly as written. Import does not
check node types, ports, configs or expressions: publishing does, and lists what is wrong.

## Limits

- The file is at most 1.99 MB as written (the import dialog counts indentation too).
- Nesting below 64 levels (a node's config starts at level 4).
- At most 25 subflow parameters.

<!-- source-stamp: transfer=34bdb9a2d3f8 -->


---

## File: references/graph-and-wiring.md

# Graph and wiring

`graph` is `{ "nodes": [...], "edges": [...], "notes": [...] }`; `notes` is optional (layout.md).

## Nodes

| key | needed | value |
|---|---|---|
| `id` | yes | unique text; a short readable slug such as `fetch_orders`. Avoid `trigger`, `params` and ids starting with `$`. |
| `type` | yes | a type from the node pages, e.g. `core.condition` |
| `version` | yes | the number from the node page heading (`core.loop@3` → `3`), as a JSON number |
| `label` | yes | unique, non-empty text, and no other node's `id`; expressions use it: `node["Fetch orders"]` |
| `config` | yes | an object; the node page lists every key. `{}` for nodes without fields |
| `position` | yes | `{ "x": number, "y": number }` (layout.md) |
| `code` | JavaScript only | the `core.javascript` snippet, on the node itself, never inside `config` |
| `disabled` | no | `true` skips the step; leave the key out otherwise |
| `settings` | no | per-step run settings, below; leave the key out unless asked |

`settings` keys, all optional: `timeoutMs` (1000–600000), `errorHandling` (`"fail"` default, or
`"continue"`: loops-and-errors.md), `retryEnabled` (boolean), `useFixedIp` (boolean),
`ifInterrupted` (`"stop"` or `"repeat"`).

## Start

Every flow has exactly one Start, written as:

```json
{ "id": "__start__", "type": "core.start", "version": 1, "label": "Start", "config": {}, "position": { "x": 0, "y": 0 } }
```

Its one port is `out`, and it is the only port that may carry several edges: wire it to each step
that should begin the run. No edge goes into Start, and Start is never disabled. A step that
Start cannot reach is dropped when the flow is published, without a warning, so check that every
node hangs off Start through the edges. Start is not a step you can name in an expression; the
trigger's data is `trigger.*` (triggers.md).

## Edges

```json
{ "source": "check_total", "sourcePort": "true", "target": "notify_sales" }
```

Exactly these three keys. A node has a single input, so there is no target port; edges have no id.

## Ports

`sourcePort` is one of the source node's ports; the node page lists them on its "Ports:" line.

| node | ports |
|---|---|
| `core.start` | `out` (one edge, to the first step) |
| `core.condition` | `true`, `false` |
| `core.switch` | each `cases[].id`, then `else` |
| `core.loop` | `body`, `done` |
| `core.tryCatch` | `try`, `success`, `catch` |
| `core.waitFor` | `resumed`, `timeout` |
| `human.approve` | each `options[].id`, then `timeout` |
| `claude/openai/gemini.classify` | each `labels[].id`, then `other` |
| `core.break`, `core.return` | none: nothing may leave them |
| every other node | `out` |

- Row ports come from the config: write the `cases` / `options` / `labels` array out in full, with
  a unique `id` on every row, even when the page shows a default. A row id is never the extra port's
  name (`else`, `timeout`, `other`).
- A port may stay unwired: that branch then ends the run's path there.
- One edge per port. To do two things after a step, chain them (A → B → C).
- Several edges may lead into one node (a join after a condition). That node runs once the steps
  that ran before it are done, and its `json` is null, so it reads earlier steps by name:
  `node["Fetch order"].body`.
- No cycles. To repeat, use `core.loop` (loops-and-errors.md).

## Branches

- `core.condition`: `expression` must give a real boolean (`{{ json.total > 100 }}`). It passes its
  input through, so the step after it sees the same `json` the condition saw.
- `core.switch`: `expression` gives a value; `cases` are tried in order with exact equality (the
  number `1` never matches the text `"1"`); no match leaves by `else`. Passes its input through.

```json
{ "id": "route", "type": "core.switch", "version": 1, "label": "Route by country",
  "config": { "expression": "{{ trigger.body.country }}",
              "cases": [ { "id": "nl", "label": "Netherlands", "value": "NL" }, { "id": "be", "label": "Belgium", "value": "BE" } ] },
  "position": { "x": 224, "y": 0 } }
```

Edges: `route` `nl` → …, `route` `be` → …, `route` `else` → … (or leave `else` unwired).

## What publishing checks

Publishing compiles the graph; every problem appears as an issue that names the step. When the
customer quotes one, look it up in troubleshooting.md. In order:

1. The graph's shape: node and edge keys, `version` a whole number, `position` present.
2. Unique ids; every edge's `source` and `target` is a node id.
3. Every `type@version` exists; every label is unique and no other node's id (this includes disabled and unreachable
   steps).
4. Ports exist; one edge per port; no cycle; one Start with something after it.
5. Loop bodies, try bodies, Break and Return placement (loops-and-errors.md).
6. Every `{{ }}` parses and names things that exist: upstream steps, declared secrets, trigger
   fields the flow's trigger carries, declared subflow parameters (expressions.md).
7. Every config value fits its field: required keys present, enums spelled exactly, numbers as
   numbers, cross-field rules met (the node page).
8. JavaScript steps have `code`; `errorHandling: "continue"` is not set on Break, Return or
   Raise error; at most one resume URL, outside loops; `core.callFlow` names a flow.

## Disabling a step

`"disabled": true` on a step with one way out (`out`) skips it: its input passes to the next step.
Routing steps (condition, switch, loop, try/catch, wait, approval, classify) and Start cannot be
disabled; delete them instead. Nothing may name a disabled step in an expression.

<!-- source-stamp: graph=fa04a94a91a1 compile=0bb4f9e964c5 -->


---

## File: references/layout.md

# Layout and sticky notes

Positions only draw the canvas; they never change what runs. A tidy layout is what the customer
sees first, so place every node.

## Positions

- Use multiples of 16. One step to the right is `x + 224`; one row down is `y + 192`.
- Start at `{ "x": 0, "y": 0 }`; a straight chain goes `224, 448, 672, …` at `y: 0`.
- After a step with several ports, put the step on port `i` (counting from 0, in the order the node
  page lists the ports, of `n` ports) at `x = source.x + 224`, `y = source.y + (i − (n − 1) / 2) × 192`.
  Two ports: `y − 96` and `y + 96`; three: `y − 192`, `y`, `y + 192`. Each branch then runs on to the
  right on its own row; a step where branches join sits right of the longest branch.
- A loop body runs right of `body`; the step after the loop hangs off `done` on the other row.
- No two nodes share a position, and two nodes closer than 96 across and 160 down overlap.
- Edit: keep every existing position. Place a new step between its neighbours (the midpoint,
  rounded to 16); if that spot is taken, move only the new step, e.g. 96 down. Keep each edge's
  `route` when the edge stays; drop it from an edge you rewire.

## Sticky notes

Notes explain the flow on the canvas. They are optional; add one only when it helps the customer
finish the setup (the after-import steps) or when they ask. In `graph`, beside `nodes` and `edges`:

```json
"notes": [ { "id": "start-here", "text": "Start here: add the webhook trigger, then publish.",
             "position": { "x": 0, "y": 224 }, "size": { "width": 240, "height": 160 } } ]
```

- `id` unique among the notes; `text` plain text with line breaks as `\n`; `size` is
  `{ width, height }` (never `w`/`h`), at least 96 × 64.
- `color`: `orange`, `green`, `blue`, `purple` or `grey`; leave it out for yellow.
- Put a note below the flow (`y` of the lowest node + 224) so it covers no node.
- Edit: keep every note as it is unless the customer asks to change it.

<!-- source-stamp: editor=b42484134ac1 -->


---

## File: references/loops-and-errors.md

# Loops, errors, waits and approvals

## Loop (`core.loop@3`)

```json
{ "id": "each_order", "type": "core.loop", "version": 3, "label": "Each order",
  "config": { "items": "{{ node['Fetch orders'].body.orders }}", "collect": "", "allPages": false },
  "position": { "x": 448, "y": 0 } }
```

- `items` is one expression that gives a list (null fails the run: `{{ json.rows ?? parseJson('[]') }}`
  when the list may be missing). It is read once, when the loop starts. At most 50,000 rounds.
- The **body** is every step reachable from the `body` port. It runs once per item and simply ends;
  the loop then takes the next item. `done` fires once, after the last round.
- In the body: `loop.item`, `loop.index` (from 0), `loop.first`, `loop.last`, `loop.count`, and in
  a loop inside a loop, `loop.parent.item`. The body's first step has `json` = null.
- Body steps can name each other and every step before the loop. They cannot name the loop itself.

Wiring rules (publishing refuses each):

- `body` is wired; `done` may stay unwired.
- No edge back to the loop or to an earlier body step.
- No edge into a body step from outside the body.
- A body path never joins the `done` path: the step after the loop hangs off `done` only.
- A step belongs to one loop. A loop inside a body is fine; its own body belongs to it.

After the loop (on `done`): `json` and `node['Each order']` are `{ itemCount, completedCount }`,
plus `collected` when `collect` is set, plus `stoppedEarly` / `stoppedAt` after a Break. A body
step named after the loop gives its **last** round only.

### Gathering results

`collect` is read after every round; the list lands in `node['Each order'].collected`, in order:
`"collect": "{{ node['Enrich contact'] }}"` or one field, `"{{ node['Enrich contact'].id }}"`. It
may read body steps, steps before the loop and `loop.*`; never `json`, secrets, `file()`,
`schema()`, or a step inside an inner loop (collect in the inner loop and read its `collected`).

### Every page

`allPages: true` makes the loop fetch the next page of a list step that pages with a cursor (a
vendor "List …" step with a cursor input and `nextCursor` output, or `table.select` with
`ifMoreRows: "first"`). `items` must then be exactly `{{ json.<list> }}` or
`{{ node['List contacts'].<list> }}` with nothing else in the field, that step runs before the loop
(not inside another loop), and its own "fetch every page" (`returnAll`) is off or absent.
`core.http` pages itself through its `pagination` config instead.

### Break

`core.break` (config `{}`) inside a body ends that loop; the flow goes on at `done`. Nothing may
leave it. Typical: body → condition → `true` → Break.

## Return and Raise error

- `core.return` ends the whole run as completed, here. `value` is the answer to a calling flow or a
  waiting webhook; leave `value` out to answer with this step's input. Nothing may leave it.
- `core.raiseError` fails the run with `message` and `code` (`^[A-Z][A-Z0-9_]*$`, default
  `FLOW_ERROR`). Inside a try body it goes to `catch` instead.
- Neither takes `settings.errorHandling`.

## Try/Catch (`core.tryCatch@1`, config `{}`)

Ports `try`, `success`, `catch`.

- The try body is everything reachable from `try`. Its first step gets the try/catch's input as `json`.
- `success` fires when the whole body finished; its `json` is null, so it reads body steps by name.
- `catch` fires when a body step fails (after its retries). Its `json` is
  `{ error: { code, message, details? }, node: { id, label, iteration? } }`: write
  `{{ json.error.message }}` and `{{ json.node.label }}`.
- The body must end on its own: no edge from a body step into the `success` or `catch` path, and no
  edge from outside into the body.
- No try/catch inside another try body. One may sit in a loop body or after `success`/`catch`.
- The `catch` path may join the `success` path.

## Carrying on after one failing step

`"settings": { "errorHandling": "continue" }` on a step makes a failure an output instead:
`{ ok: false, error: { code, message } }`. Branch after it, e.g. for an HTTP step that may return
404 (a status of 400 or more fails as `HTTP_404`):

```json
"expression": "{{ default(node['Fetch contact'].status, 0) == 200 }}"
```

Set it on the step that may fail, never on a condition or switch (there it does nothing). Use it only
when the customer asks for that step to be allowed to fail; otherwise prefer try/catch.

## Wait (`core.delay@1`)

- Wait a time: `{ "mode": "interval", "amount": 2, "unit": "hours" }`; units `seconds`, `minutes`,
  `hours`, `days`. `amount` is a whole number of at least 1 (or `{{ }}`).
- Wait until a moment: `{ "mode": "until", "until": "{{ json.sendAt }}" }`, an ISO time; a past
  time goes on at once.
- At most 31 days. The input passes through unchanged.

## Wait for a signal (`core.waitFor@1`)

```json
{ "id": "wait_reply", "type": "core.waitFor", "version": 1, "label": "Wait for reply",
  "config": { "deadline": { "amount": 3, "unit": "days" }, "onTimeout": "continue", "resumeUrlEnabled": true },
  "position": { "x": 672, "y": 0 } }
```

- Ports `resumed` and `timeout`. Deadline 1 minute to 31 days (units `minutes`, `hours`, `days`).
  `onTimeout: "fail"` fails the run instead of leaving by `timeout`.
- With `resumeUrlEnabled: true`, a step before the wait sends `{{ execution.resumeUrl }}` (in a mail
  or HTTP call); a POST to that URL resumes the run, and its JSON body arrives as
  `json.payload` on `resumed`. Output: `{ resumedBy, payload, waitedMs }`.
- At most one resume-URL wait per flow, and never inside a loop.

## Approval (`human.approve@1`)

Start from the node page's example. Ports: each `options[].id` (default `approved`, `rejected`;
2–5 options, labels up to 40 characters), plus `timeout`.

- `request.title` (up to 120 characters) and `request.body` (up to 4000) may use `{{ }}`;
  `request.data` shows extra fields to the approver.
- Approvers: people in the workspace are picked by the customer after import; a file cannot name
  them. Publishing needs at least one approver, so either tell the customer to choose them under
  "Who decides", or, when the customer gave an email address, set `request.approverAddresses` to
  `"name@example.com"` and `request.whoMayDecide` to `"members-and-outside"` (the address must be
  in the workspace's address book).
- Deadline default 7 days; `onTimeout` `continue` (leave by `timeout`) or `fail`.
- On an option port: `json.decision`, `json.decisionLabel`, `json.comment`, `json.decidedBy.name`,
  `json.decidedAt`. A test run asks nobody.

<!-- source-stamp: compile=0bb4f9e964c5 runtime=ebb2f787556b -->


---

## File: references/nodes/ERRORS.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Node error codes

Every code a step can stop a run with, by the nodes that raise it, with the sentence the run page shows. troubleshooting.md says what to change in the file for the common ones; a code marked *(troubleshooting.md)* is explained only there. `HTTP_<status>` (`HTTP_404`) is the status an HTTP step got back.

## ActiveCampaign

- `ACTIVECAMPAIGN_ACCOUNT_EXISTS` ActiveCampaign already has an account with this name — names are unique there.
- `ACTIVECAMPAIGN_AUTH` ActiveCampaign rejected the connection's API key — it stops working when its owner resets it.
- `ACTIVECAMPAIGN_CONTACT_EXISTS` ActiveCampaign already has a contact with this e-mail address.
- `ACTIVECAMPAIGN_INVALID_CONFIG` This ActiveCampaign node's settings do not fit together — its message says which two.
- `ACTIVECAMPAIGN_INVALID_ORIGIN` The connection's ActiveCampaign API URL is not an address — copy it from Settings → Developer there.
- `ACTIVECAMPAIGN_INVALID_REQUEST` ActiveCampaign refused the request — check the ids. Custom fields are keyed by id, never by label.
- `ACTIVECAMPAIGN_INVALID_RESPONSE` ActiveCampaign answered something that is not data — check the connection's API URL.
- `ACTIVECAMPAIGN_INVALID_TAG_NAME` A tag name holds a comma or a line break, which ActiveCampaign does not allow.
- `ACTIVECAMPAIGN_NOT_FOUND` ActiveCampaign has no record by that id.
- `ACTIVECAMPAIGN_NO_TAGS` No tag was given to add — fill in a tag name or a tag id.
- `ACTIVECAMPAIGN_REFUSED` ActiveCampaign accepted the call and changed nothing — open the record there to see why.
- `ACTIVECAMPAIGN_REJECTED` ActiveCampaign refused the values — check the required fields.
- `ACTIVECAMPAIGN_REQUEST` ActiveCampaign refused the request — check the node's settings.
- `ACTIVECAMPAIGN_RESPONSE_TOO_LARGE` ActiveCampaign answered more data than a run can parse — ask for a smaller page.
- `ACTIVECAMPAIGN_TAG_EXISTS` ActiveCampaign already has a tag with this name — names are case-insensitive there.
- `ACTIVECAMPAIGN_THROTTLED` ActiveCampaign is throttling this account (five calls a second) — the run waits and tries again.
- `ACTIVECAMPAIGN_TOO_MANY_RESULTS` ActiveCampaign returned more rows than a run can carry — narrow the filter, or page the read.
- `ACTIVECAMPAIGN_TOO_MANY_TAGS` ActiveCampaign takes at most 100 tags in one call — split the list.
- `ACTIVECAMPAIGN_UNAVAILABLE` ActiveCampaign could not be reached — try again once it is back.

## AFAS Profit

- `AFAS_AUTH` AFAS Profit rejected the connection's credentials — test the connection under Manage → Connections.
- `AFAS_BLOCKED` AFAS Profit refused this address — switch 'Use fixed IP address' on and allow the IP on its connection form.
- `AFAS_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `AFAS_FILE_NOT_FOUND` AFAS Profit has no such file, or the app connector may not see it — activate the SubjectConnector.
- `AFAS_FILE_TOO_LARGE` The file is larger than a run can carry — fetch it outside the flow.
- `AFAS_FILTER_MISMATCH` An AFAS Profit filter does not match its values — check the filter rows.
- `AFAS_INSECURE_ORIGIN` An AFAS Profit connection must use https — fix it under Manage → Connections.
- `AFAS_INVALID_BODY` AFAS Profit refused the body this node sent — check the fields it was built from.
- `AFAS_INVALID_CURSOR` The Cursor is not one an AFAS Profit read answered — paste an earlier Next cursor, or clear it.
- `AFAS_INVALID_ORIGIN` An AFAS Profit environment number is not a valid address — fix it under Manage → Connections.
- `AFAS_INVALID_PATH` The AFAS Profit connector path is not valid — check the node's connector field.
- `AFAS_INVALID_RESPONSE` AFAS Profit answered something that is not JSON — check the connector name.
- `AFAS_NOT_FOUND` AFAS Profit has no connector by that name — check the node's connector field.
- `AFAS_REQUEST` AFAS Profit refused the request — its own reason is in the detail.
- `AFAS_RESPONSE_TOO_LARGE` AFAS Profit answered more data than a run can parse — ask for less, page by page.
- `AFAS_THROTTLED` AFAS Profit is rate-limiting this workspace — the run will need to be spread out.
- `AFAS_TOO_MANY_ROWS` AFAS Profit returned more rows than a run can carry — filter, or page the request.
- `AFAS_UNAVAILABLE` AFAS Profit could not be reached — try again once it is back.
- `AFAS_UNVERIFIED_RESPONSE_ID` Response ids are not supported yet — AFAS has not published the URL shape, so Flowmage does not guess.

## AI

- `AI_NO_MATCH` *(troubleshooting.md)*
- `AI_OUTPUT_INVALID` *(troubleshooting.md)*

## Asana

- `ASANA_AUTH` Asana rejected the connection's token — test the connection under Manage → Connections.
- `ASANA_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `ASANA_FORBIDDEN` Asana refused the request — this token's user has no access to that project, task or workspace.
- `ASANA_INVALID_CONFIG` Two settings on this node cannot be used together — the detail says which.
- `ASANA_INVALID_REQUEST` Asana refused the request — check the ids and values. Custom fields are keyed by gid, not by name.
- `ASANA_INVALID_RESPONSE` Asana answered something that is not JSON — check the connection.
- `ASANA_NOT_FOUND` Asana has no task, project or team by that id — check the id the node was given.
- `ASANA_PREMIUM_REQUIRED` This Asana feature needs a paid plan.
- `ASANA_REQUEST` Asana refused the request — its own reason is in the detail.
- `ASANA_RESPONSE_TOO_LARGE` Asana answered more data than a run can parse — ask for fewer fields, or a smaller page.
- `ASANA_THROTTLED` Asana is rate-limiting this workspace — a rejected request still counts, so the run waits.
- `ASANA_TOO_MANY_RESULTS` Asana returned more rows than a run can carry — narrow the filter, or page the request.
- `ASANA_UNAVAILABLE` Asana could not be reached — try again once it is back.

## Bullhorn

- `BULLHORN_AUTH` Bullhorn refused the session — test the connection under Manage → Connections.
- `BULLHORN_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `BULLHORN_FILE_TOO_LARGE` The file is larger than a run can carry — fetch it outside the flow.
- `BULLHORN_FORBIDDEN` Bullhorn refused the call — the API user lacks the entitlement for it.
- `BULLHORN_INVALID_BODY` The record body is text or empty — give Bullhorn the fields as a JSON object.
- `BULLHORN_INVALID_CURSOR` The cursor is not one a Bullhorn list answered — paste its Next cursor, or clear it.
- `BULLHORN_INVALID_RESPONSE` Bullhorn answered something that is not JSON — check the connection.
- `BULLHORN_INVALID_SEGMENT` A name or id contains a character that would address a different record — remove /, ? or #.
- `BULLHORN_NOT_FOUND` Bullhorn has no record by that id.
- `BULLHORN_NO_SESSION` This connection has no Bullhorn session yet — test it under Manage → Connections.
- `BULLHORN_REQUEST` Bullhorn refused the request — its own reason is in the detail.
- `BULLHORN_RESPONSE_TOO_LARGE` Bullhorn answered more data than a run can parse — ask for fewer fields or a smaller page.
- `BULLHORN_THROTTLED` Bullhorn is throttling this workspace — wait a moment and run it again.
- `BULLHORN_TOO_MANY_ROWS` Bullhorn returned more rows than a run can carry — narrow the query, or page the request.
- `BULLHORN_UNAVAILABLE` Bullhorn could not be reached — try again once it is back.

## Carerix

- `CARERIX_AUTH` Carerix rejected the connection's token — test the connection.
- `CARERIX_EMPTY_RESULT` Carerix answered without the record — check the id and the environment.
- `CARERIX_FORBIDDEN` The Carerix client is missing a scope this call needs — add it under Identity Access.
- `CARERIX_INVALID_CONFIG` This Carerix step is missing something it cannot run without.
- `CARERIX_INVALID_REQUEST` Carerix refused the request — check the qualifier and the field names.
- `CARERIX_INVALID_RESPONSE` Carerix answered something that is not a GraphQL answer.
- `CARERIX_NOT_FOUND` Carerix has no record by that id.
- `CARERIX_PAYLOAD_TOO_LARGE` Carerix accepts 10 MB in one request — send a smaller file.
- `CARERIX_QUERY_ERROR` Carerix refused the document itself — the step names the field it choked on.
- `CARERIX_READ_ONLY_VIOLATION` That document is a mutation while the step says it only reads — change the setting.
- `CARERIX_REQUEST` Carerix refused the request — check the node's settings.
- `CARERIX_RESPONSE_TOO_LARGE` Carerix answered more than a run can parse — ask for fewer fields, or a smaller page.
- `CARERIX_THROTTLED` Carerix is throttling this client (10 requests a second) — it retries.
- `CARERIX_TOO_MANY_RESULTS` Carerix returned more records than a run can carry — narrow the qualifier, or page it.
- `CARERIX_UNAVAILABLE` Carerix could not be reached — try again once it is back.

## Clockify

- `CLOCKIFY_AUTH` Clockify rejected the API key — a key only works for the workspace it was generated in.
- `CLOCKIFY_FILE_TOO_LARGE` This Clockify report file is larger than a run can carry — ask for a shorter period.
- `CLOCKIFY_FORBIDDEN` Clockify refused this — the key's user does not have the role for it in Clockify.
- `CLOCKIFY_INVALID_CONFIG` A Clockify node's settings do not fit together — its own reason is in the detail.
- `CLOCKIFY_INVALID_HOST` The Clockify data region or subdomain on this connection is not one Clockify publishes — fix it under Manage → Connections.
- `CLOCKIFY_INVALID_REQUEST` Clockify refused the data — check the ids, the dates and any required custom fields.
- `CLOCKIFY_INVALID_RESPONSE` Clockify answered something that is not JSON — check the connection.
- `CLOCKIFY_NOT_FOUND` Clockify knows nothing with that id in this workspace.
- `CLOCKIFY_NO_USER` Clockify did not say who this API key belongs to — name a user id on the node instead.
- `CLOCKIFY_NO_WORKSPACE` This Clockify connection has no workspace to act in — set the Workspace ID, or fill in the node's override.
- `CLOCKIFY_REQUEST` Clockify refused the request — check the node's settings.
- `CLOCKIFY_RESPONSE_TOO_LARGE` Clockify answered more data than a run can parse — ask for a smaller page, or a shorter period.
- `CLOCKIFY_THROTTLED` Clockify is rate-limiting this workspace — a Free workspace gets 30 requests an hour.
- `CLOCKIFY_TOO_MANY_RESULTS` Clockify returned more rows than a run can carry — narrow the period, or read it page by page.
- `CLOCKIFY_UNAVAILABLE` Clockify could not be reached — try again once it is back.

## Core

- `FLOW_ERROR` *(troubleshooting.md)*
- `HTTP_INVALID_BODY` *(troubleshooting.md)*
- `HTTP_PAGINATION_CROSS_ORIGIN` *(troubleshooting.md)*
- `HTTP_PAGINATION_STOPPED` *(troubleshooting.md)*
- `HTTP_PAGINATION_TOO_LARGE` *(troubleshooting.md)*
- `HTTP_PAGINATION_UNUSABLE` *(troubleshooting.md)*
- `HTTP_RESPONSE_TOO_LARGE` *(troubleshooting.md)*
- `NOT_ENGINE_EXECUTED` *(troubleshooting.md)*

## Crypto

- `CRYPTO_INVALID` *(troubleshooting.md)*
- `JWT_EXPIRED` *(troubleshooting.md)*
- `JWT_INVALID` *(troubleshooting.md)*

## CSV

- `CSV_INVALID` *(troubleshooting.md)*
- `CSV_TOO_LARGE` *(troubleshooting.md)*

## DocuSign

- `DOCUSIGN_AUTH` DocuSign rejected the token — test the connection under Manage → Connections.
- `DOCUSIGN_DOCUMENT_THROTTLED` DocuSign is throttling downloads of this document — space the downloads out.
- `DOCUSIGN_DOCUMENT_TOO_LARGE` A document is over DocuSign's 32 MB upload limit.
- `DOCUSIGN_ENVELOPE_TOO_LARGE` The documents together are over DocuSign's 200 MB envelope limit.
- `DOCUSIGN_INVALID_CONFIG` This DocuSign step is missing a value it needs — nothing was sent to DocuSign.
- `DOCUSIGN_INVALID_REQUEST` DocuSign refused the request — check the ids and the values.
- `DOCUSIGN_INVALID_RESPONSE` DocuSign answered something that is not JSON — check the connection.
- `DOCUSIGN_NOT_FOUND` DocuSign has no envelope, template or document by that id.
- `DOCUSIGN_POLLING_LIMIT` DocuSign allows one status check per envelope per 15 minutes — poll less, or use a Connect webhook.
- `DOCUSIGN_REQUEST` DocuSign refused the request — its own reason is in the detail.
- `DOCUSIGN_RESPONSE_TOO_LARGE` DocuSign answered more data than a run can parse — ask for less, or a smaller page.
- `DOCUSIGN_THROTTLED` DocuSign is rate-limiting this account — the run waits and tries again.
- `DOCUSIGN_TOO_MANY_ENVELOPES` DocuSign returned more rows than a run can carry — narrow the search.
- `DOCUSIGN_TOO_MANY_RECIPIENTS` The envelope has over 100 recipients — split it.
- `DOCUSIGN_UNAVAILABLE` DocuSign could not be reached — try again once it is back.

## Email

- `SES_ATTACHMENTS_TOO_LARGE` The attachments are too large to send — send fewer, or a link.
- `SES_FROM_REQUIRED` Sending through your own SES account needs a From address — fill it in on the node.
- `SES_INVALID_ADDRESS` An e-mail address on this message is not valid — check To, CC and BCC.
- `SES_INVALID_ATTACHMENT` An attachment could not be read — check the file the node points at.
- `SES_TOO_MANY_RECIPIENTS` This message has more recipients than one send allows — split it.

## Exact Online

- `EXACT_AUTH` Exact Online refused the session — the connection needs to be reconnected.
- `EXACT_FORBIDDEN` Exact Online refused the resource — this user may not see it in this division.
- `EXACT_INVALID_BODY` The record body is empty or not an object — give Exact Online JSON fields.
- `EXACT_INVALID_CURSOR` The Cursor is not a next page of this resource — paste an earlier Next cursor, or clear it.
- `EXACT_INVALID_ORIGIN` The Exact Online country is not one of its sites — fix it under Manage → Connections.
- `EXACT_INVALID_RESOURCE` The resource path is empty — write it as Exact Online spells it.
- `EXACT_INVALID_RESPONSE` Exact Online answered something that is not JSON — check the connection.
- `EXACT_NOT_FOUND` Exact Online has no record by that id.
- `EXACT_NO_DIVISION` This connection has no administration to act in — set a division, or reconnect it.
- `EXACT_REQUEST` Exact Online refused the request — check the resource path and the values.
- `EXACT_RESPONSE_TOO_LARGE` Exact Online answered more data than a run can parse — ask for fewer fields, or a smaller page.
- `EXACT_THROTTLED` Exact Online is throttling this division (60 calls a minute) — the window has to reset.
- `EXACT_TOO_MANY_ROWS` Exact Online returned more rows than a run can carry — narrow the filter, or page the request.
- `EXACT_UNAVAILABLE` Exact Online could not be reached — try again once it is back.

## Freshdesk

- `FRESHDESK_ATTACHMENTS_TOO_LARGE` The attachments together are over Freshdesk's 20 MB limit for one call — send fewer files.
- `FRESHDESK_AUTH` Freshdesk refused the API key — check it under Manage → Connections, and that its agent is still active.
- `FRESHDESK_CONFLICT` Freshdesk sees this as a duplicate — a contact with this e-mail may already exist.
- `FRESHDESK_FILE_INVALID` An attachment's contents are empty or not base64 — check the file expression.
- `FRESHDESK_FORBIDDEN` The Freshdesk agent this key belongs to may not do this, or the plan does not include it.
- `FRESHDESK_INVALID_CONFIG` A Freshdesk setting on this node cannot be used as it stands — the node names which one.
- `FRESHDESK_INVALID_REQUEST` Freshdesk refused the values — the node names the fields it complained about.
- `FRESHDESK_INVALID_RESPONSE` Freshdesk answered something that is not JSON — check the connection's address.
- `FRESHDESK_NOT_ALLOWED` Freshdesk does not allow this here — a ticket that is deleted or spam cannot be deleted again.
- `FRESHDESK_NOT_FOUND` Freshdesk has no ticket, contact, company or agent by that id.
- `FRESHDESK_REQUEST` Freshdesk refused the request — check the node's settings.
- `FRESHDESK_RESPONSE_TOO_LARGE` Freshdesk answered more data than a run can parse — ask for fewer pages, or drop an extra.
- `FRESHDESK_THROTTLED` Freshdesk is rate-limiting this account — the run waits and tries again.
- `FRESHDESK_TOO_MANY_RESULTS` Freshdesk returned more rows than a run can carry — narrow the filter, or read it page by page.
- `FRESHDESK_UNAVAILABLE` Freshdesk could not be reached — try again once it is back.
- `FRESHDESK_UNSUPPORTED_MEDIA` Freshdesk refused how this request was packaged — that is a fault in the node, not in your settings.

## HiBob

- `HIBOB_AUTH` HiBob rejected the service user — test the connection, and check Production against Sandbox.
- `HIBOB_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `HIBOB_FILE_TOO_LARGE` This HiBob report is larger than a run can carry — export it from Bob itself.
- `HIBOB_FORBIDDEN` HiBob refused this — the service user's permission group does not reach that data.
- `HIBOB_INVALID_CONFIG` This HiBob node is set to something Bob cannot do — check its fields.
- `HIBOB_INVALID_ID` An id this flow sends HiBob is empty or is not an id — check the node's fields.
- `HIBOB_INVALID_REQUEST` HiBob refused the data — check the field ids and that every list value is one Bob knows.
- `HIBOB_INVALID_RESPONSE` HiBob answered something that is not JSON — check the connection.
- `HIBOB_NOT_FOUND` HiBob knows nothing at that address — the record is gone, or its module is switched off.
- `HIBOB_REQUEST` HiBob refused the request — check the node's settings.
- `HIBOB_RESPONSE_TOO_LARGE` HiBob answered more data than a run can parse — ask for fewer fields or fewer people.
- `HIBOB_TASK_NOT_COMPLETABLE` HiBob would not complete this task — it is most likely already done.
- `HIBOB_THROTTLED` HiBob calls are paused — HiBob throttled this endpoint, or refused too many sign-ins through the fixed IP.
- `HIBOB_TOO_MANY_RESULTS` HiBob returned more entries than a run can carry — narrow it, or page it inside a loop.
- `HIBOB_UNAVAILABLE` HiBob could not be reached — try again once it is back.

## HubSpot

- `HUBSPOT_ASSOCIATION_TYPE_UNKNOWN` HubSpot defines no standard link between these two objects — fill in an association type id.
- `HUBSPOT_AUTH` HubSpot refused the token — test the connection under Manage → Connections.
- `HUBSPOT_BATCH_TOO_LARGE` HubSpot takes at most 100 records in one batch call — send fewer.
- `HUBSPOT_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `HUBSPOT_FILE_TARGET_AMBIGUOUS` HubSpot takes a folder path or a folder id, never both.
- `HUBSPOT_INVALID_INPUT` This node expects a list of records or of ids, and got something else.
- `HUBSPOT_INVALID_REQUEST` HubSpot refused the request — a property name or value is wrong, or an admin's validation rule rejects it.
- `HUBSPOT_INVALID_RESPONSE` HubSpot answered something that is not JSON — check the connection.
- `HUBSPOT_LIST_NOT_MANUAL` Only manual and snapshot HubSpot lists can have their membership changed by a flow.
- `HUBSPOT_LOCKED` HubSpot locked these records while a large sync ran — it clears in seconds.
- `HUBSPOT_MIGRATING` The HubSpot account is being moved between data centres — this can last up to a day.
- `HUBSPOT_MISSING_SCOPE` The HubSpot private app does not have the scope this call needs — add it to the app.
- `HUBSPOT_NOT_FOUND` HubSpot has no record by that id in that object.
- `HUBSPOT_OBJECT_TYPE_REQUIRED` This node is set to a custom HubSpot object but no object type id is filled in.
- `HUBSPOT_QUERY_TOO_LONG` This search's body is over HubSpot's 3,000-character limit.
- `HUBSPOT_REQUEST` HubSpot refused the request — check the node's settings.
- `HUBSPOT_REQUEST_TOO_LONG` The request address is too long for HubSpot — ask for fewer properties or fewer ids.
- `HUBSPOT_RESPONSE_TOO_LARGE` HubSpot answered more data than a run can parse — ask for fewer properties, or a smaller page.
- `HUBSPOT_SEARCH_CEILING` HubSpot never answers past the 10,000th result of one search — narrow the filters.
- `HUBSPOT_THROTTLED` HubSpot is throttling this app — the run waits and tries again.
- `HUBSPOT_TIMEOUT` HubSpot did not answer in time — narrow the read.
- `HUBSPOT_TOO_MANY_FILTERS` This search asks for more filter groups or filters than HubSpot allows.
- `HUBSPOT_TOO_MANY_PIPELINES` Reading every pipeline's stages would take more calls than HubSpot allows at once.
- `HUBSPOT_TOO_MANY_RESULTS` HubSpot returned more records than a run can carry — narrow the read, or page it in a loop.
- `HUBSPOT_UNAVAILABLE` HubSpot could not be reached — try again once it is back.

## Jira

- `JIRA_AUTH` Jira refused the connection — an Atlassian API token expires, so check its date and reconnect.
- `JIRA_CONFLICT` Jira is already changing this object — the run waits and tries again.
- `JIRA_FILE_INVALID` The file's contents are empty or not base64 — check the file expression.
- `JIRA_FORBIDDEN` Jira refused the request — this account lacks a project permission, or issue security hides the issue.
- `JIRA_INVALID_CONFIG` A Jira setting on this node cannot be used as it stands — the node names which one.
- `JIRA_INVALID_REQUEST` Jira refused the request — the node names the fields it complained about.
- `JIRA_INVALID_RESPONSE` Jira answered something that is not JSON — check the connection's site.
- `JIRA_NOT_FOUND` Jira has no issue, project or user by that id — it also answers this when the account may not see it.
- `JIRA_REQUEST` Jira refused the request — check the node's settings.
- `JIRA_RESPONSE_TOO_LARGE` Jira answered more data than a run can parse — ask for fewer fields, or a smaller page.
- `JIRA_THROTTLED` Jira is rate-limiting this site — the run waits and tries again.
- `JIRA_TOO_LARGE` The file is over this Jira site's attachment limit, which a Jira admin sets.
- `JIRA_TOO_MANY_RESULTS` Jira returned more issues than a run can carry — narrow the JQL, or read it page by page.
- `JIRA_TRANSITION_NOT_FOUND` Jira cannot make that move from where the issue is now — the node lists the moves it can make.
- `JIRA_UNAVAILABLE` Jira could not be reached — try again once it is back.

## JSON

- `JSON_INVALID` A document this flow read is not valid JSON — check what produced it.
- `JSON_TOO_DEEP` A value nests too deeply to write as JSON text — flatten it, or pass only the part you need.
- `JSON_TOO_LARGE` A JSON document was too large to parse — ask the source for less.

## List

- `LIST_INVALID` *(troubleshooting.md)*

## Mailchimp

- `MAILCHIMP_ACCOUNT_DISABLED` This Mailchimp user is disabled — an account owner has to enable it again.
- `MAILCHIMP_AUTH` Mailchimp rejected the API key — test the connection under Manage → Connections.
- `MAILCHIMP_FORBIDDEN` Mailchimp refused this — the account's plan or this user's role does not include it.
- `MAILCHIMP_INVALID_CONFIG` This node's settings cannot be sent to Mailchimp as they are.
- `MAILCHIMP_INVALID_REQUEST` Mailchimp refused the request — check the ids and the values the node sends.
- `MAILCHIMP_INVALID_RESPONSE` Mailchimp answered something that is not JSON — check the connection.
- `MAILCHIMP_INVALID_SCHEDULE_TIME` Mailchimp only sends on the quarter hour — round the send time to :00, :15, :30 or :45.
- `MAILCHIMP_NOT_FOUND` Mailchimp knows no audience, contact, segment or campaign with that id.
- `MAILCHIMP_NO_DATACENTER` This Mailchimp connection has no datacenter code — paste the whole API key, including the -us21 part.
- `MAILCHIMP_REQUEST` Mailchimp refused the request — check the node's settings.
- `MAILCHIMP_RESPONSE_TOO_LARGE` Mailchimp answered more data than a run can parse — name the fields you want, or page it.
- `MAILCHIMP_THROTTLED` Mailchimp allows ten calls at a time per key — a loop that fans out is the usual cause.
- `MAILCHIMP_TOO_MANY_RESULTS` Mailchimp returned more rows than a run can carry — narrow the read, or page it in a loop.
- `MAILCHIMP_UNAVAILABLE` Mailchimp could not be reached — try again once it is back.
- `MAILCHIMP_WRONG_DATACENTER` This Mailchimp key belongs to another Mailchimp server — paste the whole key, dash and all.

## Mailchimp Transactional

- `MANDRILL_AUTH` Mailchimp Transactional refused the connection's API key — test it under Manage → Connections.
- `MANDRILL_FORBIDDEN` This Mailchimp key may not do that — a key can be limited to a subaccount or to sending only.
- `MANDRILL_INVALID_IMAGE` An inline image has no image file extension — Mailchimp refuses one whose type is not an image.
- `MANDRILL_INVALID_PARAMS` A JSON field on this node is not a JSON array — check its example.
- `MANDRILL_INVALID_REQUEST` Mailchimp refused the request — check the ids and the field values on this node.
- `MANDRILL_INVALID_RESPONSE` Mailchimp answered something that is not JSON — check the connection.
- `MANDRILL_INVALID_SEND_AT` The schedule time is not readable — Mailchimp wants UTC as 2026-10-01 09:00:00.
- `MANDRILL_INVALID_TAG` A tag cannot be used — at most 50 characters, and never starting with an underscore.
- `MANDRILL_NOT_FOUND` Mailchimp knows no message, address or record by that id — only recently sent mail can be looked up.
- `MANDRILL_NO_BODY` Mailchimp will not send an empty message — give it a body.
- `MANDRILL_NO_RECIPIENTS` This send names no recipient — fill in To, Cc or Bcc.
- `MANDRILL_PAYLOAD_TOO_LARGE` This call is over Mailchimp's 10 MB limit — attachments ride inside it as base64, so send fewer or smaller files.
- `MANDRILL_PAYMENT_REQUIRED` This needs a paid Transactional plan with a positive balance — scheduling a message is the usual cause.
- `MANDRILL_RECIPIENTS_REJECTED` Mailchimp refused some of the recipients — the addresses and the reasons are in this node's output.
- `MANDRILL_REQUEST` Mailchimp refused the request — check the node's settings.
- `MANDRILL_RESPONSE_TOO_LARGE` Mailchimp answered more data than a run can parse — ask for fewer messages, or narrow the search.
- `MANDRILL_SUBACCOUNT_NOT_FOUND` Mailchimp has no subaccount with that id.
- `MANDRILL_TEMPLATE_NOT_FOUND` No Mailchimp template with that slug — the slug is the immutable name, not the display name.
- `MANDRILL_THROTTLED` Mailchimp is throttling this key — message search allows 20 searches a minute and the window has to clear.
- `MANDRILL_TOO_MANY_LABELS` A Mailchimp template carries at most 10 labels.
- `MANDRILL_UNAVAILABLE` Mailchimp Transactional could not be reached — try again once it is back.

## MessageBird

- `MESSAGEBIRD_AMBIGUOUS` More than one MessageBird contact matches — use the contact id.
- `MESSAGEBIRD_AUTH` MessageBird refused the access key — test the connection under Manage → Connections.
- `MESSAGEBIRD_DUPLICATE` MessageBird already has a contact with that phone number.
- `MESSAGEBIRD_INSUFFICIENT_BALANCE` The MessageBird account is out of credit, so nothing was sent — top it up.
- `MESSAGEBIRD_INVALID_CONFIG` This MessageBird node is missing something it needs to send.
- `MESSAGEBIRD_INVALID_REQUEST` MessageBird refused the values — check the sender and the phone numbers.
- `MESSAGEBIRD_INVALID_RESPONSE` MessageBird answered something that is not JSON — check the connection.
- `MESSAGEBIRD_METHOD_NOT_ALLOWED` MessageBird refused the method for this address — this is a fault in the node.
- `MESSAGEBIRD_NOT_FOUND` MessageBird has no message, verification, contact or group by that id.
- `MESSAGEBIRD_REQUEST` MessageBird refused the request — check the node's settings.
- `MESSAGEBIRD_RESPONSE_TOO_LARGE` MessageBird answered more data than a run can parse — ask for a smaller page.
- `MESSAGEBIRD_THROTTLED` MessageBird is rate-limiting this account — the run waits and retries.
- `MESSAGEBIRD_TIMEOUT` MessageBird timed out — the request may not have been carried out.
- `MESSAGEBIRD_TOO_MANY_IDS` That is more contacts than this node adds to a group in one run — add them in batches.
- `MESSAGEBIRD_TOO_MANY_RECIPIENTS` MessageBird takes at most 50 numbers per send — split the list, or send to a group.
- `MESSAGEBIRD_TOO_MANY_RESULTS` MessageBird returned more rows than a run can carry — narrow the filter, or page the read.
- `MESSAGEBIRD_UNAVAILABLE` MessageBird could not be reached — try again once it is back.

## Microsoft Graph

- `GRAPH_AUTH` Microsoft Graph rejected the connection — test it under Manage → Connections.
- `GRAPH_FORBIDDEN` The Microsoft Graph app has no permission for this call — an admin grants it in Microsoft Entra ID.
- `GRAPH_INVALID_ADDRESS` An e-mail address or the recipient count is not valid — check the To field.
- `GRAPH_INVALID_ATTACHMENT` An attachment has no file name — give every attachment one.
- `GRAPH_INVALID_CONFIG` A field holds a value Microsoft Graph cannot take — see the detail.
- `GRAPH_INVALID_FILE` The file is empty, its contents are not base64, or it changed while it was sent — check the file.
- `GRAPH_INVALID_PATH` The user or path would address a different resource — remove /, ? or # and use an id.
- `GRAPH_INVALID_PHOTO` The photo is not a JPEG's base64 or is over 4 MB — check the Photo field.
- `GRAPH_INVALID_RESPONSE` Microsoft Graph answered something unexpected — a non-JSON body or a next-page link to another host.
- `GRAPH_NAME_TAKEN` A file already has the new folder's name — choose another name for the folder.
- `GRAPH_NOT_FOUND` Microsoft 365 has nothing by that id — check it, and that it was not deleted.
- `GRAPH_REQUEST` Microsoft Graph refused the request — its own reason is in the detail.
- `GRAPH_RESPONSE_TOO_LARGE` Microsoft Graph answered more data than a run can parse — ask for fewer fields or a smaller page.
- `GRAPH_TASK_CHANGED` Someone changed the Planner task after its ETag was read — read it again, or leave the ETag empty.
- `GRAPH_THROTTLED` Microsoft Graph is throttling this workspace — wait and run it again.
- `GRAPH_TOO_LARGE` The request was too large for Microsoft Graph — the detail names the limit.
- `GRAPH_TOO_MANY_ROWS` Microsoft Graph returned more rows than a run can carry — filter, or page the request.
- `GRAPH_UNAVAILABLE` Microsoft Graph could not be reached — try again once it is back.
- `GRAPH_UPLOAD_FAILED` Microsoft 365 did not accept a part of the file — the detail names the status and the bytes.
- `GRAPH_UPLOAD_URL_REFUSED` Microsoft Graph answered an upload address off SharePoint — no part of the file was sent.

## Mollie

- `MOLLIE_AUTH` Mollie refused the key — test the connection, and check test against live.
- `MOLLIE_FORBIDDEN` Mollie refused the resource — balances, settlements and invoices need an organization token.
- `MOLLIE_GONE` That object was deleted at Mollie and cannot be read again.
- `MOLLIE_INVALID_CONFIG` This Mollie node's settings contradict each other — open it and read the field.
- `MOLLIE_INVALID_FIELD` Mollie refused one of the values — it names the field in the detail.
- `MOLLIE_INVALID_REQUEST` Mollie refused the request — a retry after an hour reuses a key it has forgotten.
- `MOLLIE_INVALID_RESPONSE` Mollie answered something this node cannot read — check the connection.
- `MOLLIE_IN_FLIGHT` Mollie is still handling an identical call — retrying is safe.
- `MOLLIE_NOT_FOUND` Mollie has no object by that id in this mode.
- `MOLLIE_REQUEST` Mollie refused the request — check the node's settings.
- `MOLLIE_RESPONSE_TOO_LARGE` Mollie answered more data than a run can parse — ask for fewer results per call.
- `MOLLIE_THROTTLED` Mollie is throttling this merchant — the window has to reset.
- `MOLLIE_TOO_MANY_RESULTS` Mollie returned more rows than a run can carry — narrow the read, or page it in a loop.
- `MOLLIE_UNAVAILABLE` Mollie could not be reached — try again once it is back.

## Moneybird

- `MONEYBIRD_AUTH` Moneybird refused the token — test the connection under Manage → Connections.
- `MONEYBIRD_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `MONEYBIRD_FILE_TOO_LARGE` That Moneybird document is too large for a run to carry.
- `MONEYBIRD_FORBIDDEN` Moneybird refused the call — the token was made without the permission it needs.
- `MONEYBIRD_INVALID_CONFIG` This Moneybird node's settings contradict each other — open it and read the field.
- `MONEYBIRD_INVALID_FIELD` Moneybird refused some values — it names the fields in the detail.
- `MONEYBIRD_INVALID_RESPONSE` Moneybird answered something this node cannot read — check the connection.
- `MONEYBIRD_NOT_FOUND` Moneybird has no such record — check the administration id as well.
- `MONEYBIRD_NO_ADMINISTRATION` This node has no Moneybird administration — set one on the node or on the connection.
- `MONEYBIRD_REQUEST` Moneybird refused the request — check the node's settings.
- `MONEYBIRD_RESPONSE_TOO_LARGE` Moneybird answered more data than a run can parse — ask for fewer results per page.
- `MONEYBIRD_THROTTLED` Moneybird is throttling this address — it allows 150 calls per 5 minutes per IP address.
- `MONEYBIRD_TOO_MANY_RESULTS` Moneybird returned more records than a run can carry — narrow the read with a filter.
- `MONEYBIRD_UNAVAILABLE` Moneybird could not be reached — try again once it is back.

## Notion

- `NOTION_AUTH` Notion refused the integration secret — test the connection under Manage → Connections.
- `NOTION_CAPABILITY_MISSING` The Notion integration misses a capability, or nobody shared this page with it — add it under the page's Connections menu.
- `NOTION_CONFLICT` Somebody was changing this Notion page at the same moment — the run retries.
- `NOTION_CREDITS_EXHAUSTED` This Notion workspace has no credits left for that.
- `NOTION_DUPLICATE_REQUEST` Notion has already handled this request and will not repeat it.
- `NOTION_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `NOTION_FILE_TOO_LARGE` This file is larger than a one-part Notion upload carries — 20 MB is the limit.
- `NOTION_INVALID_BLOCKS` The content blocks are not a valid JSON list — check the Content field.
- `NOTION_INVALID_CONFIG` A field this Notion step needs is empty or is not an id.
- `NOTION_INVALID_PROPERTY` A property value does not fit its kind in Notion — write it in the JSON field instead.
- `NOTION_INVALID_REQUEST` Notion refused the request — check the property names and values; a computed property cannot be written.
- `NOTION_INVALID_RESPONSE` Notion answered something that is not one of its normal answers.
- `NOTION_MALFORMED_REQUEST` Notion could not read the request this node sent — a fault in the node, not in its fields.
- `NOTION_NOT_FOUND` Notion has no page or database by that id, or the integration was never given access to it.
- `NOTION_QUERY_INCOMPLETE` Notion stopped at its own 10,000-result ceiling, so this answer is not the whole set.
- `NOTION_READONLY_PROPERTY` Notion works that property out itself and refuses to be given a value.
- `NOTION_REQUEST` Notion refused the request — check the node's settings.
- `NOTION_RESPONSE_TOO_LARGE` Notion answered more data than a run can parse — ask for fewer properties, or a smaller page.
- `NOTION_ROW_LIMIT` This Notion database has reached its row limit — nothing was added.
- `NOTION_THROTTLED` Notion is throttling this connection — the run waits and tries again.
- `NOTION_TOO_MANY_BLOCKS` Notion writes at most 100 blocks in one step — split the content.
- `NOTION_TOO_MANY_RESULTS` Notion returned more pages than a run can carry — narrow the filter, or page it in a loop.
- `NOTION_UNAVAILABLE` Notion could not be reached — try again once it is back.
- `NOTION_UNKNOWN_PROPERTY` That Notion database has no property by that name — check the spelling.
- `NOTION_VERSION_MISSING` The Notion request went out without a version — test the connection.

## Oneflow

- `ONEFLOW_AUTH` Oneflow rejected the connection's credentials — test the connection under Manage → Connections.
- `ONEFLOW_FIELD_TOO_LONG` A data-field value is over Oneflow's 1,024-character maximum — shorten it.
- `ONEFLOW_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `ONEFLOW_FILE_TOO_LARGE` The file is larger than a run can carry — fetch it outside the flow, or raise the run's limit.
- `ONEFLOW_INVALID_CONFIG` The Cursor is not one a Oneflow list answered — paste an earlier Next cursor, or clear it.
- `ONEFLOW_INVALID_REQUEST` Oneflow refused the request — many fields are only editable before a contract is signed.
- `ONEFLOW_INVALID_RESPONSE` Oneflow answered something that is not JSON — check the connection.
- `ONEFLOW_NOT_FOUND` Oneflow has no contract or file by that id — check the id the node was given.
- `ONEFLOW_OFFSET_CEILING` Oneflow cannot page past 19,900 — narrow the search with a filter and read the rest separately.
- `ONEFLOW_REQUEST` Oneflow refused the request — its own reason is in the detail.
- `ONEFLOW_RESPONSE_TOO_LARGE` Oneflow answered more data than a run can parse — ask for less, page by page.
- `ONEFLOW_THROTTLED` Oneflow is rate-limiting this workspace — the run will need to be spread out.
- `ONEFLOW_TOO_MANY_CONTRACTS` Oneflow returned more contracts than a run can carry — filter, or page the request.
- `ONEFLOW_UNAVAILABLE` Oneflow could not be reached — try again once it is back.

## OTYS

- `OTYS_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `OTYS_INVALID_CURSOR` The cursor is not one an OTYS list answered — paste its Next cursor, or clear it.
- `OTYS_INVALID_METHOD` The service or method name is not an OTYS identifier — check it at ows.otys.nl/info.
- `OTYS_INVALID_PARAMS` The fields are text or empty — give OTYS a JSON object.
- `OTYS_INVALID_RESPONSE` OTYS answered something that is not JSON — check the connection.
- `OTYS_REQUEST` OTYS refused the call — the JSON-RPC error code and the method name are in the detail.
- `OTYS_RESPONSE_TOO_LARGE` OTYS answered more data than a run can parse — ask for fewer fields, or a smaller page.
- `OTYS_SESSION` OTYS says this session is no longer valid — the connection re-logs in on the next attempt.
- `OTYS_THROTTLED` OTYS is rate-limiting this API key — a rejected request still counts, so the run waits.
- `OTYS_TOO_MANY_ROWS` OTYS returned more rows than a run can carry — narrow the filter, or page the request.
- `OTYS_UNAVAILABLE` OTYS could not be reached — try again once it is back.
- `OTYS_UPLOAD_FAILED` OTYS's file service stored nothing — the file was not uploaded.

## Personio

- `PERSONIO_AUTH` Personio rejected the connection's token — test the connection.
- `PERSONIO_CONFLICT` Personio refused because the record clashes with one it already has, or a file is not ready.
- `PERSONIO_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `PERSONIO_FILE_TOO_LARGE` Personio takes recruiting documents up to 20 MB — send a smaller file.
- `PERSONIO_FORBIDDEN` Personio refused this — the integration misses this endpoint or the attribute is not readable.
- `PERSONIO_INVALID_CONFIG` This Personio node is set to something Personio cannot do — check its fields.
- `PERSONIO_INVALID_ID` An id this flow sends Personio is empty or is not an id — check the node's fields.
- `PERSONIO_INVALID_REQUEST` Personio refused the data — check the ids, the dates and the attribute ids.
- `PERSONIO_INVALID_RESPONSE` Personio answered something this node cannot read — check the connection.
- `PERSONIO_NOT_FOUND` Personio knows no record with that id.
- `PERSONIO_PRECONDITION` This Personio account does not have the org-structure feature this node needs.
- `PERSONIO_REQUEST` Personio refused the request — check the node's settings.
- `PERSONIO_RESPONSE_TOO_LARGE` Personio answered more data than a run can carry — narrow the read, or page it in a loop.
- `PERSONIO_THROTTLED` Personio is throttling this connection — it will work again shortly.
- `PERSONIO_TOO_MANY_RESULTS` Personio returned more records than a run can carry — narrow it, or page it inside a loop.
- `PERSONIO_UNAVAILABLE` Personio could not be reached — try again once it is back.

## Pipedrive

- `PIPEDRIVE_AUTH` Pipedrive refused the API token — test the connection under Manage → Connections.
- `PIPEDRIVE_ENDPOINT_RETIRED` Pipedrive has retired this API endpoint — this node needs updating.
- `PIPEDRIVE_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `PIPEDRIVE_FILE_TARGET_REQUIRED` This file has no record to attach to — fill in the id of the record it belongs to.
- `PIPEDRIVE_FILE_TOO_LARGE` This Pipedrive file is larger than a run can carry — fetch it outside the flow.
- `PIPEDRIVE_FORBIDDEN` Pipedrive refused the request — this token's user may not see or change this record.
- `PIPEDRIVE_INVALID_BODY` Pipedrive accepts JSON bodies only on its v2 API.
- `PIPEDRIVE_INVALID_CURSOR` The cursor is not one a previous page answered — feed it the list's own Next cursor.
- `PIPEDRIVE_INVALID_DOMAIN` The Pipedrive company domain is not the first part of a Pipedrive address — fix it under Manage → Connections.
- `PIPEDRIVE_INVALID_REQUEST` Pipedrive refused the request — check the ids and the field values.
- `PIPEDRIVE_INVALID_RESPONSE` Pipedrive answered something that is not one of its envelopes — check the company domain.
- `PIPEDRIVE_LEAD_LINK_REQUIRED` A Pipedrive lead hangs on a person or on an organization — fill in at least one.
- `PIPEDRIVE_NOTE_TARGET_REQUIRED` This note has no record to hang on — fill in the id of the record it belongs to.
- `PIPEDRIVE_NOT_FOUND` Pipedrive has no record by that id.
- `PIPEDRIVE_PRODUCT_PRICE_MISSING` This product has no price in the deal's currency — fill in the price per unit.
- `PIPEDRIVE_REFUSED` Pipedrive answered the request but refused the change — check the ids.
- `PIPEDRIVE_REQUEST` Pipedrive refused the request — check the node's settings.
- `PIPEDRIVE_RESPONSE_TOO_LARGE` Pipedrive answered more data than a run can parse — ask for fewer fields, or a smaller page.
- `PIPEDRIVE_SEARCH_TERM_TOO_SHORT` Pipedrive needs a longer search term — two characters, or one with exact match on.
- `PIPEDRIVE_THROTTLED` Pipedrive is throttling this company — the run waits and tries again.
- `PIPEDRIVE_TOO_MANY_CUSTOM_FIELDS` Pipedrive reads at most 15 custom fields in one call — ask for fewer.
- `PIPEDRIVE_TOO_MANY_PIPELINES` Reading every pipeline's stages would take more calls than Pipedrive allows at once.
- `PIPEDRIVE_TOO_MANY_RESULTS` Pipedrive returned more records than a run can carry — narrow the read, or page it in a loop.
- `PIPEDRIVE_UNAVAILABLE` Pipedrive could not be reached — try again once it is back.

## Recruitee

- `RECRUITEE_ATTACHMENT_INVALID` The attachment has no file or address, or the picked file is not base64 — the detail says which.
- `RECRUITEE_AUTH` Recruitee rejected the connection's credentials — test the connection under Manage → Connections.
- `RECRUITEE_EMPTY_UPDATE` Nothing to change — fill at least one field on the node.
- `RECRUITEE_INVALID_CURSOR` The cursor is not one a Recruitee search answered — paste its Next cursor, or clear it.
- `RECRUITEE_INVALID_ORIGIN` The Recruitee company ID is not a valid address part — fix it under Manage → Connections.
- `RECRUITEE_INVALID_RESPONSE` Recruitee answered something that is not JSON — check the company ID and the token.
- `RECRUITEE_NOT_FOUND` Recruitee has no record by that id — check the id the node was given.
- `RECRUITEE_REQUEST` Recruitee refused the request — its own reason is in the detail.
- `RECRUITEE_RESPONSE_TOO_LARGE` Recruitee answered more data than a run can parse — ask for less, page by page.
- `RECRUITEE_THROTTLED` Recruitee is rate-limiting this workspace — the run will need to be spread out.
- `RECRUITEE_TOO_MANY_RESULTS` Recruitee returned more rows than a run can carry — filter, or page the request.
- `RECRUITEE_UNAVAILABLE` Recruitee could not be reached — try again once it is back.
- `RECRUITEE_VALIDATION` Recruitee refused the values — the fields it named are in the detail.

## Recruition

- `RECRUITION_AUTH` Recruition refused the session — test the connection under Manage → Connections; an access token that expired needs a new one pasted there.
- `RECRUITION_FORBIDDEN` Recruition refused the call — this API user has no access to it.
- `RECRUITION_INSECURE_ORIGIN` A Recruition address must use https — fix it under Manage → Connections.
- `RECRUITION_INVALID_BODY` The body is text or empty — give Recruition the record as a JSON object.
- `RECRUITION_INVALID_CURSOR` The cursor is not one a Recruition find answered — paste its Next cursor, or clear it.
- `RECRUITION_INVALID_ORIGIN` The Recruition address is not a valid subdomain or hostname — fix it under Manage → Connections.
- `RECRUITION_INVALID_RESPONSE` Recruition answered something that is not JSON — check the connection.
- `RECRUITION_NOT_FOUND` Recruition has no record by that id.
- `RECRUITION_REQUEST` Recruition refused the request — its own reason is in the detail.
- `RECRUITION_RESPONSE_TOO_LARGE` Recruition answered more data than a run can parse — ask for fewer fields, or a smaller page.
- `RECRUITION_THROTTLED` Recruition is throttling this workspace — the run will need to be spread out.
- `RECRUITION_TOO_MANY_PAGES` Recruition still had pages past the limit — narrow the filters, or raise Stop after.
- `RECRUITION_UNAVAILABLE` Recruition could not be reached — try again once it is back.
- `RECRUITION_VALIDATION` Recruition refused the values — one of its own rules rejected them. Check the field names and ids.

## Schema

- `SCHEMA_NOT_FOUND` *(troubleshooting.md)*
- `SCHEMA_PATH_INVALID` *(troubleshooting.md)*

## Several nodes

- `CUSTOM_NODE_CONFIG_FIELD_RESERVED` *(troubleshooting.md)*
- `CUSTOM_NODE_NOT_RUNNABLE` *(troubleshooting.md)*
- `MULTIPART_INVALID_CONTENT_TYPE` *(troubleshooting.md)*
- `PAGE_CURSOR_REPEATED` *(troubleshooting.md)*
- `PAGE_RATE_WINDOW_FULL` *(troubleshooting.md)*
- `PATH_SEGMENT_INVALID` *(troubleshooting.md)*

## Slack

- `SLACK_AUTH` Slack refused the bot token — reinstall the Slack app and paste the new token under Manage → Connections.
- `SLACK_CHANNEL_ARCHIVED` That Slack channel is archived — unarchive it, or pick another channel.
- `SLACK_EDIT_WINDOW_CLOSED` Slack no longer allows that message to be edited.
- `SLACK_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `SLACK_FORBIDDEN` A Slack workspace policy does not allow this app to do that.
- `SLACK_INVALID_BLOCKS` The Block Kit layout is not a valid list of at most 50 blocks — check the Blocks field.
- `SLACK_INVALID_CONFIG` A field this Slack step needs is empty or outside what Slack allows.
- `SLACK_INVALID_CURSOR` The Slack page cursor expired — run the read again.
- `SLACK_INVALID_REQUEST` Slack refused the request — check the ids and the field values.
- `SLACK_INVALID_RESPONSE` Slack answered something that is not one of its normal answers.
- `SLACK_METHOD_RETIRED` Slack has retired this API method — this node needs updating.
- `SLACK_MISSING_SCOPE` The Slack app is missing a permission this step needs — add the scope and reinstall the app.
- `SLACK_NAME_TAKEN` A Slack channel with that name already exists — look it up instead of creating it.
- `SLACK_NOT_FOUND` Slack has no channel, message, file or person by that id — a private channel also looks missing until the bot joins it.
- `SLACK_NOT_IN_CHANNEL` The Flowmage bot is not in that Slack channel — invite it, or allow it to post in public channels.
- `SLACK_NOT_PINNABLE` Slack does not allow that message to be pinned.
- `SLACK_NO_PERMISSION` The Slack app lacks a permission this step needs — add the scope, or ask a workspace admin.
- `SLACK_REQUEST` Slack refused the request — check the node's settings.
- `SLACK_RESPONSE_TOO_LARGE` Slack answered more data than a run can parse — read fewer messages per call.
- `SLACK_SCHEDULE_OUT_OF_RANGE` Slack schedules a message between now and 120 days from now — pick a time inside that window.
- `SLACK_THROTTLED` Slack is throttling this workspace — the run waits and tries again.
- `SLACK_TOO_MANY_RESULTS` Slack returned more items than a run can carry — narrow the read, or page it in a loop.
- `SLACK_UNAVAILABLE` Slack could not be reached — try again once it is back.
- `SLACK_UPLOAD_FAILED` Slack's upload address did not accept the file — the run tries again.
- `SLACK_UPLOAD_REFUSED` Slack refused the upload — the workspace blocks this file type, or has no storage left.
- `SLACK_UPLOAD_URL_REFUSED` Slack answered an upload address Flowmage does not trust — nothing was sent.

## SOAP web service

- `SOAP_AUTH` The service did not let the call in — check its sign-in, or ask its owner to allow the fixed IP.
- `SOAP_CONTRACT_CHANGED` The service's description has changed — press Test on the connection, then check this step.
- `SOAP_FAULT` The service refused the call — what it said is in the detail.
- `SOAP_INVALID_CONFIG` The call does not match the service's description — the detail names the operation or parameter.
- `SOAP_INVALID_RESPONSE` The service answered something this connection cannot read — the detail says what.
- `SOAP_NOT_FOUND` Nothing answers at the service address — check it under Manage → Connections.
- `SOAP_REQUEST` The service refused the request — the HTTP status is in the detail.
- `SOAP_RESPONSE_TOO_LARGE` The service answered more data than a run can parse — ask for less per call.
- `SOAP_THROTTLED` The service asked to slow down — the run waits before it tries again.
- `SOAP_UNAVAILABLE` The service could not be reached — try again once it is back.

## Table

- `TABLE_NO_MATCH` No row matched this step's filters — loosen one, or let the step carry on.
- `TABLE_ROW_AMBIGUOUS` More than one row matched the row this step changes — pick a column whose values are unique.
- `TABLE_ROW_NO_MATCH` No row matched the row this step changes — nothing was changed.
- `TABLE_WHICH_ROW_MISSING` This step does not say which row to change — pick a column or give a Row id.

## Teamtailor

- `TEAMTAILOR_AUTH` Teamtailor rejected the API key — check the key and the data stack under Manage → Connections.
- `TEAMTAILOR_FILE_INVALID` Nothing was attached in Teamtailor — the file was empty or not base64, or Teamtailor sent back no address for it.
- `TEAMTAILOR_FORBIDDEN` Teamtailor refused this — candidates, applications and notes need an Admin read/write key.
- `TEAMTAILOR_INVALID_CONFIG` A Teamtailor node's settings do not fit together — its own reason is in the detail.
- `TEAMTAILOR_INVALID_REQUEST` Teamtailor refused the data — check the required fields and that every related id exists.
- `TEAMTAILOR_INVALID_RESPONSE` Teamtailor answered something this node could not read — check the connection.
- `TEAMTAILOR_NOT_FOUND` Teamtailor knows no record with that id.
- `TEAMTAILOR_REQUEST` Teamtailor refused the request — check the node's settings.
- `TEAMTAILOR_RESPONSE_TOO_LARGE` Teamtailor answered more data than a run can parse — ask for a smaller page, or include fewer related records.
- `TEAMTAILOR_THROTTLED` Teamtailor is rate-limiting this key — 50 requests per 10 seconds, shared by every flow on the connection.
- `TEAMTAILOR_TOO_MANY_RESULTS` Teamtailor returned more records than a run can carry — narrow the filter, or read it page by page.
- `TEAMTAILOR_UNAVAILABLE` Teamtailor could not be reached — a very large answer can also fail this way, so try a smaller page.
- `TEAMTAILOR_VERSION` Teamtailor retired the API version Flowmage pins — this needs a Flowmage update, so please report it.

## TOPdesk

- `TOPDESK_AUTH` TOPdesk rejected the connection — the login name, the application password or its expiry is wrong.
- `TOPDESK_FIELD_TOO_LONG` The brief description is over TOPdesk's 80-character maximum — shorten it.
- `TOPDESK_FILE_INVALID` The file contents are empty or not base64 — paste the file's base64.
- `TOPDESK_FILE_TOO_LARGE` The file is larger than a run can carry — fetch it outside the flow, or raise the run's limit.
- `TOPDESK_FORBIDDEN` TOPdesk refused the request — the operator needs API Access > REST API, or the call is outside their filters.
- `TOPDESK_INSECURE_ORIGIN` A TOPdesk connection must use https — fix it under Manage → Connections.
- `TOPDESK_INVALID_ORIGIN` The TOPdesk address is not a valid hostname — fix it under Manage → Connections.
- `TOPDESK_INVALID_REQUEST` TOPdesk refused the request — drop-down values must match the spelling TOPdesk uses.
- `TOPDESK_INVALID_RESPONSE` TOPdesk answered something that is not JSON — check the address.
- `TOPDESK_NOT_FOUND` TOPdesk has no call, person or attachment by that id.
- `TOPDESK_REQUEST` TOPdesk refused the request — its own reason is in the detail.
- `TOPDESK_RESPONSE_TOO_LARGE` TOPdesk answered more data than a run can parse — ask for fewer fields, or a smaller page.
- `TOPDESK_TENANT_NOT_FOUND` TOPdesk answered a web page instead of data — check the address under Manage → Connections.
- `TOPDESK_THROTTLED` TOPdesk is throttling this workspace — the run will need to be spread out.
- `TOPDESK_TOO_MANY_RESULTS` TOPdesk returned more rows than a run can carry — narrow the query, or page the request.
- `TOPDESK_UNAVAILABLE` TOPdesk could not be reached — try again once it is back.
- `TOPDESK_UNSUPPORTED_VERSION` TOPdesk refused the response format — this TOPdesk may be too old for the API version.

## Twinfield

- `TWINFIELD_AUTH` Twinfield rejected the sign-in — connect it again under Manage → Connections.
- `TWINFIELD_CLUSTER_UNKNOWN` Flowmage does not know this Twinfield login's cluster yet — press Test on the connection.
- `TWINFIELD_FORBIDDEN` This Twinfield login may not do that in this company.
- `TWINFIELD_INVALID_CONFIG` This Twinfield node is not filled in yet — its own message says which part.
- `TWINFIELD_INVALID_REQUEST` The Twinfield document cannot be sent as written — check the element names and the characters in it.
- `TWINFIELD_INVALID_RESPONSE` Twinfield answered something that is not a SOAP document — check the connection.
- `TWINFIELD_NO_COMPANY` Twinfield needs a company code on every call but the offices list — fill one in.
- `TWINFIELD_REFUSED` Twinfield took the call and refused it — the field or code it objected to is in the detail.
- `TWINFIELD_REQUEST` Twinfield refused the request — check the node's settings.
- `TWINFIELD_RESPONSE_TOO_LARGE` Twinfield answered more data than a run can parse — ask per period, or for fewer columns.
- `TWINFIELD_THROTTLED` Twinfield is throttling this client — an unregistered one gets 25 credits a minute per administration.
- `TWINFIELD_TOO_MANY_LINES` A Twinfield transaction carries at most 1,000 lines — split it.
- `TWINFIELD_TOO_MANY_RESULTS` Twinfield returned more rows than a run can carry — narrow the period or the search.
- `TWINFIELD_UNAVAILABLE` Twinfield could not be reached — try again once it is back.

## XML

- `XML_INVALID` *(troubleshooting.md)*
- `XML_TOO_LARGE` *(troubleshooting.md)*

## Zendesk

- `ZENDESK_AUTH` Zendesk refused the connection's token — test it under Manage → Connections.
- `ZENDESK_CONFLICT` Someone changed the same Zendesk record at once — read it again and re-apply.
- `ZENDESK_FILE_INVALID` The file this Zendesk upload sends is empty or is not base64.
- `ZENDESK_FORBIDDEN` Zendesk refused the request — this connection's user may not see it, or its client lacks the permission.
- `ZENDESK_INVALID_CONFIG` A setting on this Zendesk node is not one Zendesk can act on.
- `ZENDESK_INVALID_HOST` This connection's Zendesk subdomain is not one — fix it under Manage → Connections.
- `ZENDESK_INVALID_REQUEST` Zendesk refused the values — check the ids and the fields on the node.
- `ZENDESK_INVALID_RESPONSE` Zendesk answered something that is not JSON — check the connection.
- `ZENDESK_NOT_FOUND` Zendesk has no ticket, person, organization or group with that id.
- `ZENDESK_REQUEST` Zendesk refused the request — check the node's settings.
- `ZENDESK_RESPONSE_TOO_LARGE` Zendesk answered more data than a run can parse — ask for a smaller page, or drop the extras.
- `ZENDESK_THROTTLED` Zendesk is throttling this account — the window has to reset.
- `ZENDESK_TOO_MANY_RESULTS` Zendesk returned more records than a run can carry — narrow the query, or read the changes since a moment.
- `ZENDESK_UNAVAILABLE` Zendesk could not be reached — try again once it is back.


---

## File: references/nodes/INDEX.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Node index

Flow file format version 1. 828 node versions in 48 families. Catalogue stamp `f9b958b66232`.

Find the family, then read its page. A node's `type` is `<family>.<action>`.

- [activecampaign.md](activecampaign.md) ActiveCampaign: addContactToAutomation, addTagsToContact, createAccount, createContact, createContactNote, createDeal, createTag, deleteContact, deleteDeal, getContact, getDeal, linkContactToAccount, listAccounts, listAutomations, listContacts, listDeals, listFields, listLists, listPipelines, listTags, removeContactFromAutomation, removeTagFromContact, setContactFieldValue, setListSubscription, updateContact, updateDeal, upsertContact
- [afas.md](afas.md) AFAS Profit: delete, describeConnector, downloadFile, get, getSubject, insert, listConnectors, runReport, update, uploadFile
- [asana.md](asana.md) Asana: addComment, addTaskToProject, completeTask, createTask, deleteTask, getProject, getTask, getTeam, listAttachments, listComments, listCustomFields, listProjects, listSections, listTasks, moveTaskToSection, searchTasks, updateTask, uploadAttachment
- [bullhorn.md](bullhorn.md) Bullhorn: associate, createEntity, deleteEntity, deleteFile, dissociate, getEntity, getFile, getMeta, listFiles, query, search, updateEntity, updateFile, uploadFile
- [carerix.md](carerix.md) Carerix: applyCandidate, createActivity, createCandidate, createCompany, createContact, createMatch, createVacancy, downloadAttachment, getCandidate, getVacancy, listActivities, listAttachments, listCandidates, listCompanies, listContacts, listMatches, listPublications, listUsers, listVacancies, query, sendTemplateEmail, updateCandidate, updateCompany, updateMatch, updateVacancy, uploadAttachment
- [claude-nodes.md](claude-nodes.md) Claude: classify, extract, generate
- [clockify.md](clockify.md) Clockify: createClient, createProject, createTag, createTask, createTimeEntry, createTimeOffRequest, deleteTimeEntry, detailedReport, getTimeOffBalance, listClients, listCustomFields, listProjects, listRunningTimers, listTags, listTasks, listTimeEntries, listTimeOffPolicies, listTimeOffRequests, listUsers, listWorkspaces, setTimeOffRequestStatus, stopTimer, summaryReport, updateProject, updateTask, updateTimeEntry, weeklyReport
- [core.md](core.md) Core: break, callFlow, condition, delay, http, javascript, log, loop, raiseError, return, switch, tryCatch, waitFor
- [crypto.md](crypto.md) crypto: hash, hmac, jwtSign, jwtVerify
- [csv.md](csv.md) csv: build, parse
- [docusign.md](docusign.md) DocuSign: correctRecipient, createSigningLink, downloadDocument, getEnvelope, listDocuments, listEnvelopes, listRecipients, listTemplates, sendDocuments, sendDraftEnvelope, sendFromTemplate, voidEnvelope
- [exact.md](exact.md) Exact Online: create, createInvoice, delete, get, listDivisions, update
- [file.md](file.md) file: delete, list, read, write
- [freshdesk.md](freshdesk.md) Freshdesk: addNote, addTimeEntry, createContact, createTicket, findCompanies, findContacts, getContact, getTicket, listAgents, listConversations, listGroups, listTicketFields, listTickets, replyToTicket, searchTickets, updateContact, updateTicket
- [gemini.md](gemini.md) Gemini: classify, extract, generate
- [graph.md](graph.md) Microsoft Graph: addGroupMember, addGroupOwner, assignLicense, cancelEvent, createEvent, createFolder, createGroup, createListItem, createPlannerTask, createUser, deleteDriveItem, deleteEvent, deleteGroup, deleteListItem, deleteUser, downloadFile, forwardMessage, getDriveItem, getEvent, getGroup, getGroupChanges, getManager, getMessage, getSchedule, getSite, getUser, getUserChanges, getUserPhoto, listAttachments, listDirectReports, listEvents, listFolderItems, listGroupMembers, listGroupOwners, listGroups, listListItems, listLists, listMailFolders, listMessages, listPlanTasks, listSubscribedSkus, listUserGroups, listUsers, moveMessage, removeGroupMember, removeLicense, removeManager, replyToMessage, request, restoreUser, revokeSignInSessions, searchFiles, sendMail, setManager, setUserPhoto, updateEvent, updateGroup, updateListItem, updateMessage, updatePlannerTask, updateUser, uploadFile, uploadLargeFile
- [hibob.md](hibob.md) HiBob: cancelTimeOffRequest, completeTask, createEmployee, createEmployeeTableEntry, createTimeOffRequest, downloadReport, getEmployee, getTimeOffBalance, inviteEmployee, listBulkTableEntries, listCustomTableEntries, listCustomTableMetadata, listEmployeeDocuments, listEmployeeFields, listEmployeeTableEntries, listReports, listTasks, listTimeOffChanges, listTimeOffPolicyTypes, listWhosOut, readCompanyList, searchApplications, searchCandidates, searchEmployees, searchJobOpenings, setEmployeeStartDate, terminateEmployee, updateEmployee, uploadEmployeeDocument
- [hubspot.md](hubspot.md) HubSpot: associateRecords, batchCreateRecords, batchReadRecords, batchUpdateRecords, createNote, createRecord, createTask, deleteRecord, dissociateRecords, getRecord, listAssociations, listListMembers, listLists, listOwners, listPipelines, listProperties, listRecords, searchRecords, updateListMembership, updateRecord, uploadFile, upsertRecord
- [human.md](human.md) human: approve
- [jira.md](jira.md) Jira: addComment, addWorklog, assignIssue, createIssue, deleteIssue, findUsers, getIssue, linkIssues, listComments, listFields, listIssueTypes, listProjects, listTransitions, searchIssues, transitionIssue, updateIssue, uploadAttachment
- [json.md](json.md) json: build, parse, stringify
- [list.md](list.md) list: dedupe, filter, group, limit, sort
- [mailchimp.md](mailchimp.md) Mailchimp: addMember, addMemberEvent, addMemberNote, archiveMember, createCampaign, createMergeField, createSegment, deleteCampaign, deleteMemberPermanently, deleteSegment, getAccount, getAudience, getBatchStatus, getCampaign, getCampaignContent, getCampaignReport, getMember, getSegment, getTemplate, listAudiences, listCampaignReports, listCampaigns, listInterestCategories, listInterests, listMemberActivity, listMemberEvents, listMemberTags, listMembers, listMergeFields, listSegments, listTags, listTemplates, replicateCampaign, scheduleCampaign, searchMembers, sendCampaign, sendTestEmail, setCampaignContent, setMemberTags, setStaticSegmentMembers, startBatch, triggerJourneyStep, unscheduleCampaign, updateCampaign, updateMember, updateSegment, upsertMember
- [mandrill.md](mandrill.md) Mailchimp Transactional: addSuppression, cancelScheduled, createTemplate, deleteTemplate, getAccountInfo, getMessage, getMessageContent, getSubaccount, getTemplate, listScheduled, listSubaccounts, listSuppressions, listTags, listTemplates, removeSuppression, renderTemplate, rescheduleMessage, searchMessages, sendEmail, sendTemplate, updateTemplate
- [messagebird.md](messagebird.md) MessageBird: addContactsToGroup, checkVerificationCode, createContact, deleteContact, getBalance, getContact, getHlr, getMessage, listContacts, listConversationMessages, listConversations, listGroups, listMessages, lookupNumber, requestHlr, sendConversationMessage, sendSms, sendVerificationCode, updateContact
- [mollie.md](mollie.md) Mollie: cancelPayment, cancelRefund, cancelSubscription, createCapture, createCustomer, createCustomerPayment, createMandate, createPayment, createPaymentLink, createRefund, createSalesInvoice, createSubscription, deleteCustomer, getCurrentProfile, getCustomer, getPayment, getPaymentLink, listBalanceTransactions, listChargebacks, listCustomers, listInvoices, listMandates, listMethods, listPaymentLinks, listPayments, listRefunds, listSalesInvoices, listSettlements, listSubscriptions, revokeMandate, updateCustomer, updatePayment
- [moneybird.md](moneybird.md) Moneybird: createContact, createPurchaseInvoice, createSalesInvoice, createTimeEntry, deleteContact, downloadSalesInvoicePdf, getContact, getSalesInvoice, linkFinancialMutation, listAdministrations, listContacts, listFinancialMutations, listLedgerAccounts, listProducts, listPurchaseInvoices, listSalesInvoices, listTaxRates, listTimeEntries, listWorkflows, registerSalesInvoicePayment, sendSalesInvoice, updateContact, updateSalesInvoice, uploadPurchaseInvoiceAttachment
- [mssql.md](mssql.md) SQL Server: query
- [mysql.md](mysql.md) MySQL: query
- [notion.md](notion.md) Notion: appendBlocks, archivePage, createComment, createPage, deleteBlock, getDataSource, getPage, getUser, listBlocks, listComments, listDataSources, listUsers, queryDataSource, search, updateBlock, updatePage, uploadFile
- [oneflow.md](oneflow.md) Oneflow: addParticipant, addParty, createContract, deleteContract, downloadFile, getContract, listContracts, listDataFields, listFiles, listTemplates, markContractSigned, publishContract, setDataFields, updateContract, uploadFile
- [openai.md](openai.md) OpenAI: classify, extract, generate
- [otys.md](otys.md) OTYS: attachFile, call, create, delete, downloadFile, get, getOptions, listFiles, search, update, uploadFile
- [personio.md](personio.md) Personio: createAbsence, createApplication, createAttendance, createCompensation, createPerson, deleteAbsence, deleteAttendance, deletePerson, downloadDocument, getAbsence, getApplication, getPerson, listAbsenceTypes, listAbsences, listApplicationStages, listApplications, listAttendances, listCandidates, listCompensationTypes, listCompensations, listDocuments, listEmployments, listOrgData, listPersons, listProjects, listRecruitingJobs, updateAbsence, updateAttendance, updateEmployment, updatePerson, uploadApplicationDocument
- [pipedrive.md](pipedrive.md) Pipedrive: addProductToDeal, createActivity, createDeal, createLead, createNote, createOrganization, createPerson, deleteRecord, downloadFile, getRecord, listActivityTypes, listFieldDefinitions, listLeads, listNotes, listPipelines, listRecords, listUsers, search, updateActivity, updateDeal, updateLead, updateOrganization, updatePerson, uploadFile
- [postgres.md](postgres.md) PostgreSQL: query
- [recruitee.md](recruitee.md) Recruitee: addNote, createCandidate, findCandidates, getCandidate, getJob, listJobs, moveCandidate, setCandidateField, updateCandidate, uploadAttachment
- [recruition.md](recruition.md) Recruition: addFile, addMemo, createApplication, createCandidate, createTask, createVacancy, deleteCandidate, findApplications, findCandidates, findFiles, findVacancies, getApplication, getCandidate, getCurrentUser, getVacancy, updateApplication, updateCandidate, updateVacancy
- [schema.md](schema.md) schema: get, set, setPath
- [ses.md](ses.md) Email: send
- [slack.md](slack.md) Slack: addReaction, createChannel, deleteMessage, findUserByEmail, getChannel, getUser, inviteToChannel, listChannelMembers, listChannels, listMessages, listThreadReplies, listUsers, openDirectMessage, pinMessage, scheduleMessage, sendEphemeralMessage, sendMessage, updateMessage, uploadFile
- [soap.md](soap.md) SOAP web service: callOperation
- [table.md](table.md) table: delete, insert, select, update
- [teamtailor.md](teamtailor.md) Teamtailor: createCandidate, createJob, createJobApplication, createNote, getCandidate, getJob, listCandidates, listCustomFieldValues, listCustomFields, listDepartments, listJobApplications, listJobs, listLocations, listNotes, listPartnerResults, listRejectReasons, listStages, listUploads, listUsers, moveJobApplication, setCustomFieldValue, updateCandidate, updateCustomFieldValue, updateJob, uploadCandidateFile
- [topdesk.md](topdesk.md) TOPdesk: addIncidentAction, archiveIncident, createIncident, downloadAttachment, escalateIncident, findCaller, getIncident, getProgressTrail, listAttachments, listIncidentActions, listIncidents, listSelectionValues, registerTimeSpent, updateIncident, uploadAttachment
- [twinfield.md](twinfield.md) Twinfield: browse, createSalesInvoice, deleteDimension, find, findDimensions, listOffices, postTransaction, processXml, readBrowseDefinition, readDimension, readSalesInvoice, readTransaction, upsertDimension
- [xml.md](xml.md) xml: build, parse
- [zendesk.md](zendesk.md) Zendesk: addComment, createOrUpdateOrganization, createOrUpdateUser, createTicket, getTicket, getUser, listChangedTickets, listComments, listGroups, listOrganizations, listTicketFields, listTickets, listUsers, search, updateTicket, uploadAttachment


---

## File: references/nodes/activecampaign.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# ActiveCampaign nodes (`activecampaign.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `activecampaign.addContactToAutomation@1` — ActiveCampaign — Start automation

Starts an ActiveCampaign automation for a contact.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `contactId` | text, default `""` | Contact id: Fill in the id or the e-mail address, not both. |
| `contactEmail` | text, default `""` | Contact e-mail address: ActiveCampaign creates the contact when this address is not known yet, which the id form cannot do. |
| `automationIds` | text, required | Automation ids: Comma-separated, up to 25. The List automations node answers these ids. |

Output: `contactAutomation` object — Automation run · `contactAutomationId` string — Automation run id · `automationCount` number — Automations started.

```json
{"type":"activecampaign.addContactToAutomation","version":1,"label":"Start automation","config":{"contactId":"","contactEmail":"","connection":"<connection id>","automationIds":"<automation ids>"}}
```

## `activecampaign.addTagsToContact@1` — ActiveCampaign — Tag contact

Adds up to 100 tags to an ActiveCampaign contact in one call.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `contactId` | text, required | Contact id |
| `tagNames` | text, default `""` | Tag names: Comma-separated names. A tag name may not contain a comma, and ActiveCampaign treats names as case-insensitive. |
| `tagIds` | text, default `""` | Tag ids: Comma-separated ids, from the List tags node. Names and ids may be mixed. |
| `createMissing` | true/false, default `true` | Create tags that do not exist yet: Off, a name ActiveCampaign does not know is refused instead of created. |

Output: `contactTags` array — Tag links · `tagCount` number — Tags on the contact after this call.

```json
{"type":"activecampaign.addTagsToContact","version":1,"label":"Tag contact","config":{"tagNames":"","tagIds":"","createMissing":true,"connection":"<connection id>","contactId":"<contact id>"}}
```

## `activecampaign.createAccount@1` — ActiveCampaign — Create account

Creates a company record in ActiveCampaign's CRM.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `name` | text, required | Name: Account names are unique in ActiveCampaign — a second one is refused. |
| `accountUrl` | text, default `""` | Website |
| `ownerId` | text, default `""` | Owner id: The ActiveCampaign user this account belongs to. |
| `fields` | object of text, default `{}` | Custom fields: Keys are the account custom field's id, from ActiveCampaign's account field settings. |

Output: `accountId` string — Account id · `account` object — Account.

```json
{"type":"activecampaign.createAccount","version":1,"label":"Create account","config":{"accountUrl":"","ownerId":"","fields":{},"connection":"<connection id>","name":"<name>"}}
```

## `activecampaign.createContact@1` — ActiveCampaign — Add contact

Adds an ActiveCampaign contact and refuses when the e-mail address is already known.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `email` | text, default `""` | E-mail address — its `{{ }}` must give a string |
| `allowNullEmail` | true/false, default `false` | Allow a contact without an e-mail address: ActiveCampaign requires an e-mail address unless this is on. A contact without one can only be reached by phone. |
| `firstName` | text, default `""` | First name |
| `lastName` | text, default `""` | Last name |
| `phone` | text, default `""` | Phone |
| `fieldValues` | object of text, default `{}` | Custom fields: Keys are the field id from the List custom fields node — ActiveCampaign does not accept a field's label here. |
| `tags` | text, default `""` | Tags: Comma-separated tag names. A tag name may not contain a comma. |
| `listId` | text, default `""` | List id: Subscribes the new contact to this list in the same call. — a value from the vendor's own list |
| `listAutoresponder` | "default" or "send" or "suppress", default `"default"` | Welcome autoresponder — used when `listId` is not "" |
| `accountName` | text, default `""` | Account name: Links the contact to this CRM account. Account names are unique there. |
| `createAccountIfMissing` | true/false, default `false` | Create the account when it does not exist — used when `accountName` is not "" |
| `optInIp` | text, default `""` | Opt-in IP address: The address the person opted in from. ActiveCampaign accepts IPv4 only. |

Output: `contactId` string — Contact id · `contact` object — Contact · `fieldValues` array — Custom field values.

```json
{"type":"activecampaign.createContact","version":1,"label":"Add contact","config":{"email":"","allowNullEmail":false,"firstName":"","lastName":"","phone":"","fieldValues":{},"tags":"","listId":"","listAutoresponder":"default","accountName":"","createAccountIfMissing":false,"optInIp":"","connection":"<connection id>"}}
```

## `activecampaign.createContactNote@1` — ActiveCampaign — Write note

Writes a note on an ActiveCampaign contact.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `contactId` | text, required | Contact id |
| `note` | text, required | Note |
| `listId` | text, default `""` | List id: Scopes the note to one list, which is what decides who hears ActiveCampaign's note event. — a value from the vendor's own list |

Output: `note` object — Note · `noteId` string — Note id.

```json
{"type":"activecampaign.createContactNote","version":1,"label":"Write note","config":{"listId":"","connection":"<connection id>","contactId":"<contact id>","note":"<note>"}}
```

## `activecampaign.createDeal@1` — ActiveCampaign — Create deal

Creates a deal in an ActiveCampaign pipeline.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `title` | text, required | Title |
| `description` | text, default `""` | Description |
| `contactId` | text, default `""` | Contact id: A deal needs a contact or an account. Fill in at least one of the two. |
| `accountId` | text, default `""` | Account id |
| `value` | number, default `0` | Value, in cents: ActiveCampaign stores a deal's value in cents: 456.78 is written as 45678. |
| `currency` | text, default `"eur"` | Currency: A three-letter ISO code. The node sends it lowercased, as ActiveCampaign wants. |
| `pipelineId` | text, default `""` | Pipeline id: ActiveCampaign calls a pipeline a deal group. Fill in a pipeline or a stage — the List pipelines node answers both ids. — a value from the vendor's own list |
| `stageId` | text, default `""` | Stage id |
| `ownerId` | text, default `""` | Owner id: The ActiveCampaign user the deal belongs to, unless the pipeline assigns one. |
| `percent` | number, default `0` | Chance of winning, in percent |
| `status` | number, default `0` | Status: ActiveCampaign's own status number: 0 open, 1 won, 2 lost. Its list is on the deal-statuses page in the developer documentation. |
| `fields` | object of text, default `{}` | Custom fields: Keys are the deal custom field's id, never its label. |

Output: `dealId` string — Deal id · `deal` object — Deal.

```json
{"type":"activecampaign.createDeal","version":1,"label":"Create deal","config":{"description":"","contactId":"","accountId":"","value":0,"currency":"eur","pipelineId":"","stageId":"","ownerId":"","percent":0,"status":0,"fields":{},"connection":"<connection id>","title":"<title>"}}
```

## `activecampaign.createTag@1` — ActiveCampaign — Create tag

Creates one tag in ActiveCampaign so contacts can be labelled with it.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `tag` | text, required | Name: A name may not contain a comma. ActiveCampaign treats names as case-insensitive in practice, so My Tag and my tag collide. |
| `tagType` | "contact" or "template", default `"contact"` | Kind |
| `description` | text, default `""` | Description |

Output: `tagId` string — Tag id · `tag` object — Tag.

```json
{"type":"activecampaign.createTag","version":1,"label":"Create tag","config":{"tagType":"contact","description":"","connection":"<connection id>","tag":"<name>"}}
```

## `activecampaign.deleteContact@1` — ActiveCampaign — Delete contact

Removes an ActiveCampaign contact and every link it has.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `contactId` | text, required | Contact id: The Find contacts node answers this id. |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — It was already gone · `contactId` string — Contact id.

```json
{"type":"activecampaign.deleteContact","version":1,"label":"Delete contact","config":{"connection":"<connection id>","contactId":"<contact id>"}}
```

## `activecampaign.deleteDeal@1` — ActiveCampaign — Delete deal

Removes a deal from ActiveCampaign.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `dealId` | text, required | Deal id |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — It was already gone · `dealId` string — Deal id.

```json
{"type":"activecampaign.deleteDeal","version":1,"label":"Delete deal","config":{"connection":"<connection id>","dealId":"<deal id>"}}
```

## `activecampaign.getContact@1` — ActiveCampaign — Get contact

Reads one ActiveCampaign contact with its custom field values, lists and tags.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `contactId` | text, required | Contact id: The Find contacts node answers this id. |

Output: `contactId` string — Contact id · `contact` object — Contact · `fieldValues` array — Custom field values · `contactLists` array — List memberships · `contactTags` array — Tag links · `geoIps` array — Known addresses.

```json
{"type":"activecampaign.getContact","version":1,"label":"Get contact","config":{"connection":"<connection id>","contactId":"<contact id>"}}
```

## `activecampaign.getDeal@1` — ActiveCampaign — Get deal

Reads one ActiveCampaign deal by its id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `dealId` | text, required | Deal id: The Find deals node answers this id. |

Output: `dealId` string — Deal id · `deal` object — Deal · `related` object — Side-loaded relations.

```json
{"type":"activecampaign.getDeal","version":1,"label":"Get deal","config":{"connection":"<connection id>","dealId":"<deal id>"}}
```

## `activecampaign.linkContactToAccount@1` — ActiveCampaign — Link contact to account

Links an ActiveCampaign contact to a company record in the CRM.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `accountId` | text, required | Account id: The List accounts node answers this id. |
| `contactId` | text, required | Contact id |
| `jobTitle` | text, default `""` | Job title: What this person does at that company. Optional. |

Output: `accountContact` object — Link · `accountContactId` string — Link id.

```json
{"type":"activecampaign.linkContactToAccount","version":1,"label":"Link contact to account","config":{"jobTitle":"","connection":"<connection id>","accountId":"<account id>","contactId":"<contact id>"}}
```

## `activecampaign.listAccounts@2` — ActiveCampaign — List accounts

Lists the company records in ActiveCampaign's CRM.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `limit` | number, default `100` | Accounts per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 rows rather than answering a short one. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `accounts` array — Accounts · `accountCount` number — Accounts in this answer · `total` number — Accounts the CRM has · `hasMore` boolean — More accounts are waiting · `nextOffset` number — Offset for the next page · `related` object — Side-loaded relations · `nextCursor` string — Cursor for the next page.

```json
{"type":"activecampaign.listAccounts","version":2,"label":"List accounts","config":{"limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `activecampaign.listAccounts@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `limit` | number, default `100` | Accounts per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 rows rather than answering a short one. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `activecampaign.listAutomations@2` — ActiveCampaign — List automations

Lists the automations an ActiveCampaign account can start for a contact.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `limit` | number, default `100` | Automations per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 rows rather than answering a short one. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `automations` array — Automations · `automationCount` number — Automations in this answer · `total` number — Automations the account has · `hasMore` boolean — More automations are waiting · `nextOffset` number — Offset for the next page · `related` object — Side-loaded relations · `nextCursor` string — Cursor for the next page.

```json
{"type":"activecampaign.listAutomations","version":2,"label":"List automations","config":{"limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `activecampaign.listAutomations@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `limit` | number, default `100` | Automations per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 rows rather than answering a short one. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `activecampaign.listContacts@2` — ActiveCampaign — Find contacts

Finds ActiveCampaign contacts by e-mail, list, tag, segment or free text.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `email` | text, default `""` | E-mail address: The whole address, matched exactly. |
| `emailLike` | text, default `""` | E-mail contains: Part of an address — everyone at one domain, for instance. |
| `listId` | text, default `""` | On list — a value from the vendor's own list |
| `tagId` | text, default `""` | With tag id |
| `segmentId` | text, default `""` | In segment |
| `automationId` | text, default `""` | In automation: The List automations node answers this id. |
| `status` | "any" or "unconfirmed" or "active" or "unsubscribed" or "bounced", default `"any"` | Subscription status |
| `search` | text, default `""` | Free text: Matches names, addresses and phone numbers. |
| `createdAfter` | text, default `""` | Created after |
| `createdBefore` | text, default `""` | Created before |
| `updatedAfter` | text, default `""` | Updated after |
| `updatedBefore` | text, default `""` | Updated before |
| `limit` | number, default `100` | Contacts per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole result, and refuses past 5,000 contacts rather than answering a short list. ActiveCampaign allows the whole account five requests a second, so that walk is 50 calls and ten seconds at best. |
| `fastPaging` | true/false, default `true` | Use ActiveCampaign's fast paging: Walks by ascending id instead of by offset, which is what ActiveCampaign recommends: accounts with many contacts get slower answers as the offset grows. Turn it off for a stable offset window. — used when `returnAll` is true |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `contacts` array — Contacts · `contactCount` number — Contacts in this answer · `total` number — Contacts the search matches · `hasMore` boolean — More contacts are waiting · `nextOffset` number — Offset for the next page · `related` object — Side-loaded relations · `nextCursor` string — Cursor for the next page.

```json
{"type":"activecampaign.listContacts","version":2,"label":"Find contacts","config":{"email":"","emailLike":"","listId":"","tagId":"","segmentId":"","automationId":"","status":"any","search":"","createdAfter":"","createdBefore":"","updatedAfter":"","updatedBefore":"","limit":100,"offset":0,"returnAll":false,"fastPaging":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `activecampaign.listContacts@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `email` | text, default `""` | E-mail address: The whole address, matched exactly. |
| `emailLike` | text, default `""` | E-mail contains: Part of an address — everyone at one domain, for instance. |
| `listId` | text, default `""` | On list — a value from the vendor's own list |
| `tagId` | text, default `""` | With tag id |
| `segmentId` | text, default `""` | In segment |
| `automationId` | text, default `""` | In automation: The List automations node answers this id. |
| `status` | "any" or "unconfirmed" or "active" or "unsubscribed" or "bounced", default `"any"` | Subscription status |
| `search` | text, default `""` | Free text: Matches names, addresses and phone numbers. |
| `createdAfter` | text, default `""` | Created after |
| `createdBefore` | text, default `""` | Created before |
| `updatedAfter` | text, default `""` | Updated after |
| `updatedBefore` | text, default `""` | Updated before |
| `limit` | number, default `100` | Contacts per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole result, and refuses past 5,000 contacts rather than answering a short list. ActiveCampaign allows the whole account five requests a second, so that walk is 50 calls and ten seconds at best. |
| `fastPaging` | true/false, default `true` | Use ActiveCampaign's fast paging: Walks by ascending id instead of by offset, which is what ActiveCampaign recommends: accounts with many contacts get slower answers as the offset grows. Turn it off for a stable offset window. — used when `returnAll` is true |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"email":"","emailLike":"","listId":"","tagId":"","segmentId":"","automationId":"","status":"any","search":"","createdAfter":"","createdBefore":"","updatedAfter":"","updatedBefore":"","limit":100,"offset":0,"returnAll":false,"fastPaging":true,"connection":"<connection id>"}
{"email":"","emailLike":"","listId":"","tagId":"","segmentId":"","automationId":"","status":"any","search":"","createdAfter":"","createdBefore":"","updatedAfter":"","updatedBefore":"","limit":100,"offset":0,"returnAll":false,"fastPaging":true,"cursor":"","connection":"<connection id>"}
```

## `activecampaign.listDeals@2` — ActiveCampaign — Find deals

Finds ActiveCampaign deals by stage, pipeline, owner, tag, value or free text.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `search` | text, default `""` | Free text: Matches the deal and the contact it belongs to. |
| `searchField` | text, default `""` | Search only in: One field name, as ActiveCampaign spells it. Empty searches every field. — used when `search` is not "" |
| `title` | text, default `""` | Title contains |
| `stageId` | text, default `""` | In stage |
| `pipelineId` | text, default `""` | In pipeline: ActiveCampaign calls a pipeline a deal group. — a value from the vendor's own list |
| `status` | text, default `""` | Status: ActiveCampaign's own status number: 0 open, 1 won, 2 lost. Empty is any. |
| `ownerId` | text, default `""` | Owner id |
| `tagId` | text, default `""` | With tag id |
| `minimumValue` | text, default `""` | Value from, in cents |
| `maximumValue` | text, default `""` | to |
| `createdAfter` | text, default `""` | Created after |
| `createdBefore` | text, default `""` | Created before |
| `updatedAfter` | text, default `""` | Updated after |
| `updatedBefore` | text, default `""` | Updated before |
| `sortBy` | "none" or "title" or "value" or "created" or "contactName" or "contactOrganisation", default `"none"` | Order by |
| `sortDirection` | "asc" or "desc", default `"asc"` | Direction |
| `limit` | number, default `100` | Deals per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole result, and refuses past 5,000 deals rather than answering a short list. The account shares five requests a second. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `deals` array — Deals · `dealCount` number — Deals in this answer · `total` number — Deals the search matches · `hasMore` boolean — More deals are waiting · `nextOffset` number — Offset for the next page · `related` object — Side-loaded relations · `nextCursor` string — Cursor for the next page.

```json
{"type":"activecampaign.listDeals","version":2,"label":"Find deals","config":{"search":"","searchField":"","title":"","stageId":"","pipelineId":"","status":"","ownerId":"","tagId":"","minimumValue":"","maximumValue":"","createdAfter":"","createdBefore":"","updatedAfter":"","updatedBefore":"","sortBy":"none","sortDirection":"asc","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `activecampaign.listDeals@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `search` | text, default `""` | Free text: Matches the deal and the contact it belongs to. |
| `searchField` | text, default `""` | Search only in: One field name, as ActiveCampaign spells it. Empty searches every field. — used when `search` is not "" |
| `title` | text, default `""` | Title contains |
| `stageId` | text, default `""` | In stage |
| `pipelineId` | text, default `""` | In pipeline: ActiveCampaign calls a pipeline a deal group. — a value from the vendor's own list |
| `status` | text, default `""` | Status: ActiveCampaign's own status number: 0 open, 1 won, 2 lost. Empty is any. |
| `ownerId` | text, default `""` | Owner id |
| `tagId` | text, default `""` | With tag id |
| `minimumValue` | text, default `""` | Value from, in cents |
| `maximumValue` | text, default `""` | to |
| `createdAfter` | text, default `""` | Created after |
| `createdBefore` | text, default `""` | Created before |
| `updatedAfter` | text, default `""` | Updated after |
| `updatedBefore` | text, default `""` | Updated before |
| `sortBy` | "none" or "title" or "value" or "created" or "contactName" or "contactOrganisation", default `"none"` | Order by |
| `sortDirection` | "asc" or "desc", default `"asc"` | Direction |
| `limit` | number, default `100` | Deals per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole result, and refuses past 5,000 deals rather than answering a short list. The account shares five requests a second. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"search":"","searchField":"","title":"","stageId":"","pipelineId":"","status":"","ownerId":"","tagId":"","minimumValue":"","maximumValue":"","createdAfter":"","createdBefore":"","updatedAfter":"","updatedBefore":"","sortBy":"none","sortDirection":"asc","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"search":"","searchField":"","title":"","stageId":"","pipelineId":"","status":"","ownerId":"","tagId":"","minimumValue":"","maximumValue":"","createdAfter":"","createdBefore":"","updatedAfter":"","updatedBefore":"","sortBy":"none","sortDirection":"asc","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `activecampaign.listFields@2` — ActiveCampaign — List custom fields

Lists the custom field definitions ActiveCampaign contacts can hold.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `personalisationTag` | text, default `""` | Personalisation tag: One field, by the %TAG% name ActiveCampaign uses in e-mails — written here without the percent signs. |
| `limit` | number, default `100` | Fields per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 rows rather than answering a short one. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `fields` array — Custom fields · `fieldCount` number — Fields in this answer · `total` number — Fields the account has · `hasMore` boolean — More fields are waiting · `nextOffset` number — Offset for the next page · `related` object — Side-loaded relations · `nextCursor` string — Cursor for the next page.

```json
{"type":"activecampaign.listFields","version":2,"label":"List custom fields","config":{"personalisationTag":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `activecampaign.listFields@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `personalisationTag` | text, default `""` | Personalisation tag: One field, by the %TAG% name ActiveCampaign uses in e-mails — written here without the percent signs. |
| `limit` | number, default `100` | Fields per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 rows rather than answering a short one. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"personalisationTag":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"personalisationTag":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `activecampaign.listLists@2` — ActiveCampaign — List mailing lists

Lists the mailing lists an ActiveCampaign account sends to.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `name` | text, default `""` | Name contains: Part of a list's name. Leave it empty for every list. |
| `sort` | "none" or "nameAsc" or "nameDesc", default `"none"` | Order |
| `limit` | number, default `100` | Lists per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 rows rather than answering a short one. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `lists` array — Mailing lists · `listCount` number — Lists in this answer · `total` number — Lists the account has · `hasMore` boolean — More lists are waiting · `nextOffset` number — Offset for the next page · `related` object — Side-loaded relations · `nextCursor` string — Cursor for the next page.

```json
{"type":"activecampaign.listLists","version":2,"label":"List mailing lists","config":{"name":"","sort":"none","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `activecampaign.listLists@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `name` | text, default `""` | Name contains: Part of a list's name. Leave it empty for every list. |
| `sort` | "none" or "nameAsc" or "nameDesc", default `"none"` | Order |
| `limit` | number, default `100` | Lists per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 rows rather than answering a short one. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"name":"","sort":"none","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"name":"","sort":"none","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `activecampaign.listPipelines@2` — ActiveCampaign — List pipelines

Lists ActiveCampaign's deal pipelines, with their stages when you ask for them.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `includeStages` | true/false, default `true` | Include each pipeline's stages: Costs one extra call for the whole answer, and gives every pipeline a stages list with the ids the deal nodes want. |
| `limit` | number, default `100` | Pipelines per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 rows rather than answering a short one. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `pipelines` array — Pipelines · `pipelineCount` number — Pipelines in this answer · `total` number — Pipelines the account has · `hasMore` boolean — More pipelines are waiting · `nextOffset` number — Offset for the next page · `nextCursor` string — Cursor for the next page.

```json
{"type":"activecampaign.listPipelines","version":2,"label":"List pipelines","config":{"includeStages":true,"limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `activecampaign.listPipelines@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `includeStages` | true/false, default `true` | Include each pipeline's stages: Costs one extra call for the whole answer, and gives every pipeline a stages list with the ids the deal nodes want. |
| `limit` | number, default `100` | Pipelines per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 rows rather than answering a short one. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"includeStages":true,"limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"includeStages":true,"limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `activecampaign.listTags@2` — ActiveCampaign — List tags

Lists the tags an ActiveCampaign account knows, with their ids.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `limit` | number, default `100` | Tags per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 tags rather than answering a short one. The account shares five requests a second. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `tags` array — Tags · `tagCount` number — Tags in this answer · `total` number — Tags the account has · `hasMore` boolean — More tags are waiting · `nextOffset` number — Offset for the next page · `related` object — Side-loaded relations · `nextCursor` string — Cursor for the next page.

```json
{"type":"activecampaign.listTags","version":2,"label":"List tags","config":{"limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `activecampaign.listTags@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `limit` | number, default `100` | Tags per call: 1 to 100. ActiveCampaign's own default is 20. |
| `offset` | number, default `0` | Start at: Zero-based. Ignored while every page is fetched. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the whole list, and refuses past 5,000 tags rather than answering a short one. The account shares five requests a second. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `activecampaign.removeContactFromAutomation@1` — ActiveCampaign — Stop automation

Stops the most recent run of an ActiveCampaign automation for a contact.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `contactId` | text, default `""` | Contact id: Fill in the id or the e-mail address, not both. |
| `contactEmail` | text, default `""` | Contact e-mail address |
| `automationId` | text, required | Automation id: Only the contact's most recent active run of this automation is stopped. Run the node again for an earlier one. |

Output: `removed` boolean — Stopped by this run · `alreadyGone` boolean — There was no active run · `automationId` string — Automation id.

```json
{"type":"activecampaign.removeContactFromAutomation","version":1,"label":"Stop automation","config":{"contactId":"","contactEmail":"","connection":"<connection id>","automationId":"<automation id>"}}
```

## `activecampaign.removeTagFromContact@1` — ActiveCampaign — Untag contact

Takes one tag off an ActiveCampaign contact, leaving the tag itself in place.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `contactId` | text, required | Contact id |
| `tagId` | text, required | Tag id: The List tags node answers this id. Only the link is removed; the tag stays. |

Output: `removed` boolean — Removed by this run · `alreadyGone` boolean — The contact did not have it · `contactId` string — Contact id · `tagId` string — Tag id.

```json
{"type":"activecampaign.removeTagFromContact","version":1,"label":"Untag contact","config":{"connection":"<connection id>","contactId":"<contact id>","tagId":"<tag id>"}}
```

## `activecampaign.setContactFieldValue@1` — ActiveCampaign — Set custom field

Writes one custom field value on an ActiveCampaign contact.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `contactId` | text, required | Contact id |
| `fieldId` | text, required | Field id: The id from the List custom fields node — ActiveCampaign does not accept a field's label here. |
| `value` | text, default `""` | Value: A multi-select field takes ActiveCampaign's own \|\|first\|\|second\|\| form; this node passes the text through exactly as written. |
| `useDefaults` | true/false, default `false` | Fill the contact's other required fields with their defaults: ActiveCampaign's own option, for a contact that is missing required values. |

Output: `fieldValue` object — Field value · `fieldValueId` string — Field value id.

```json
{"type":"activecampaign.setContactFieldValue","version":1,"label":"Set custom field","config":{"value":"","useDefaults":false,"connection":"<connection id>","contactId":"<contact id>","fieldId":"<field id>"}}
```

## `activecampaign.setListSubscription@1` — ActiveCampaign — Subscribe or unsubscribe

Subscribes an ActiveCampaign contact to a list, or unsubscribes it.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `contactId` | text, required | Contact id |
| `listId` | text, required | List id: The List mailing lists node answers this id. — a value from the vendor's own list |
| `action` | "subscribe" or "unsubscribe", default `"subscribe"` | What to do: Subscribing a contact who is currently unsubscribed puts somebody back on a list they took themselves off. ActiveCampaign allows it and warns about it. |
| `isResubscribe` | true/false, default `false` | Record this as a re-subscribe: Marks the membership as a deliberate re-subscribe in ActiveCampaign. — used when `action` is "subscribe" |

Output: `contactList` object — List membership · `subscribed` boolean — Subscribed after this call.

```json
{"type":"activecampaign.setListSubscription","version":1,"label":"Subscribe or unsubscribe","config":{"action":"subscribe","isResubscribe":false,"connection":"<connection id>","contactId":"<contact id>","listId":"<list id>"}}
```

## `activecampaign.updateContact@1` — ActiveCampaign — Update contact

Changes the fields of an ActiveCampaign contact you already hold the id of.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `contactId` | text, required | Contact id: The Find contacts node answers this id. |
| `email` | text, default `""` | E-mail address: Leave it empty to keep the address the contact already has. — its `{{ }}` must give a string |
| `firstName` | text, default `""` | First name |
| `lastName` | text, default `""` | Last name |
| `phone` | text, default `""` | Phone |
| `fieldValues` | object of text, default `{}` | Custom fields: Keys are the field id from the List custom fields node — ActiveCampaign does not accept a field's label here. |
| `tags` | text, default `""` | Tags: Comma-separated tag names to add. Tags the contact already has are left alone; use Untag contact to take one off. |
| `listId` | text, default `""` | List id: Subscribes the contact to this list in the same call. |
| `listAutoresponder` | "default" or "send" or "suppress", default `"default"` | Welcome autoresponder — used when `listId` is not "" |
| `accountName` | text, default `""` | Account name: Links the contact to this CRM account. Account names are unique there. |
| `createAccountIfMissing` | true/false, default `false` | Create the account when it does not exist — used when `accountName` is not "" |
| `optInIp` | text, default `""` | Opt-in IP address: The address the person opted in from. ActiveCampaign accepts IPv4 only. |

Output: `contactId` string — Contact id · `contact` object — Contact · `fieldValues` array — Custom field values.

```json
{"type":"activecampaign.updateContact","version":1,"label":"Update contact","config":{"email":"","firstName":"","lastName":"","phone":"","fieldValues":{},"tags":"","listId":"","listAutoresponder":"default","accountName":"","createAccountIfMissing":false,"optInIp":"","connection":"<connection id>","contactId":"<contact id>"}}
```

## `activecampaign.updateDeal@1` — ActiveCampaign — Update deal

Changes a deal in ActiveCampaign, including moving it to another stage.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `dealId` | text, required | Deal id |
| `title` | text, default `""` | Title: Every field below is left as it is when you leave it empty. |
| `description` | text, default `""` | Description |
| `contactId` | text, default `""` | Contact id |
| `accountId` | text, default `""` | Account id |
| `value` | text, default `""` | Value, in cents: ActiveCampaign stores a deal's value in cents: 456.78 is written as 45678. |
| `currency` | text, default `""` | Currency: A three-letter ISO code, sent lowercased. |
| `pipelineId` | text, default `""` | Pipeline id: ActiveCampaign calls a pipeline a deal group. |
| `stageId` | text, default `""` | Stage id: Filling in only this moves the deal to another stage and changes nothing else. |
| `ownerId` | text, default `""` | Owner id |
| `percent` | text, default `""` | Chance of winning, in percent |
| `status` | text, default `""` | Status: ActiveCampaign's own status number: 0 open, 1 won, 2 lost. Its list is on the deal-statuses page in the developer documentation. |
| `fields` | object of text, default `{}` | Custom fields: Keys are the deal custom field's id, never its label. |

Output: `dealId` string — Deal id · `deal` object — Deal.

```json
{"type":"activecampaign.updateDeal","version":1,"label":"Update deal","config":{"title":"","description":"","contactId":"","accountId":"","value":"","currency":"","pipelineId":"","stageId":"","ownerId":"","percent":"","status":"","fields":{},"connection":"<connection id>","dealId":"<deal id>"}}
```

## `activecampaign.upsertContact@1` — ActiveCampaign — Create or update contact

Creates an ActiveCampaign contact, or updates the one with this e-mail address.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `activecampaign`) |
| `email` | text, required | E-mail address: The address ActiveCampaign looks the contact up by, and creates it under. — its `{{ }}` must give a string |
| `firstName` | text, default `""` | First name |
| `lastName` | text, default `""` | Last name |
| `phone` | text, default `""` | Phone |
| `fieldValues` | object of text, default `{}` | Custom fields: Keys are the field id from the List custom fields node — ActiveCampaign does not accept a field's label here. |
| `tags` | text, default `""` | Tags: Comma-separated tag names. Names that do not exist yet are created. A tag name may not contain a comma. |
| `listId` | text, default `""` | List id: Subscribes the contact to this list in the same call. |
| `listAutoresponder` | "default" or "send" or "suppress", default `"default"` | Welcome autoresponder: ActiveCampaign rejects a call that asks for both, so only the choice you make here is sent. — used when `listId` is not "" |
| `accountName` | text, default `""` | Account name: Links the contact to this CRM account. Account names are unique in ActiveCampaign. |
| `createAccountIfMissing` | true/false, default `false` | Create the account when it does not exist — used when `accountName` is not "" |
| `optInIp` | text, default `""` | Opt-in IP address: The address the person opted in from. ActiveCampaign accepts IPv4 only. |

Output: `contactId` string — Contact id · `contact` object — Contact · `fieldValues` array — Custom field values.

```json
{"type":"activecampaign.upsertContact","version":1,"label":"Create or update contact","config":{"firstName":"","lastName":"","phone":"","fieldValues":{},"tags":"","listId":"","listAutoresponder":"default","accountName":"","createAccountIfMissing":false,"optInIp":"","connection":"<connection id>","email":"<e-mail address>"}}
```


---

## File: references/nodes/afas.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# AFAS Profit nodes (`afas.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `afas.delete@1` — AFAS Profit — Delete record

Deletes a record from AFAS Profit, like a debtor or an employee.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `connector` | text, required | UpdateConnector: The node sends it a DELETE — AFAS's verb for removing a record. — a value from the vendor's own list |
| `path` | text, required | Delete path: Three steps, separated by slashes: the element, the element with its key field, and the key's value — <Element>/<Element>@<KeyField>/<value>. |

Output: `deleted` boolean — Deleted.

```json
{"type":"afas.delete","version":1,"label":"Delete record","config":{"connection":"<connection id>","connector":"<updateconnector>","path":"<delete path>"}}
```

## `afas.describeConnector@1` — AFAS Profit — Describe connector

Lists the fields, types and labels of one AFAS Profit GetConnector.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `connector` | text, required | GetConnector — a value from the vendor's own list |

Output: `fields` array — Fields · `fieldCount` number — Field count.

```json
{"type":"afas.describeConnector","version":1,"label":"Describe connector","config":{"connection":"<connection id>","connector":"<getconnector>"}}
```

## `afas.downloadFile@1` — AFAS Profit — Download file

Downloads a file attached to an AFAS Profit dossier item.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `fileId` | text, required | File id: The file's GUID, from a GetConnector over Bestanden bij dossier. |
| `fileName` | text, required | File name: The file's ORIGINAL name in AFAS Profit — a name of your own is refused. |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"afas.downloadFile","version":1,"label":"Download file","config":{"connection":"<connection id>","fileId":"<file id>","fileName":"<file name>"}}
```

## `afas.get@3` — AFAS Profit — Get records

Reads rows from AFAS Profit, like employees or debtors.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `connector` | text, required | GetConnector — a value from the vendor's own list |
| `take` | number, default `100` | Max rows: Rows per call, 1 to 1,000 — or -1 with Skip rows -1 for every row in one call. With Fetch every page on, this is the page size. |
| `skip` | number, default `0` | Skip rows: How many rows to pass over first — pairs with Max rows to read page by page. Set both to -1 for every row in one call. |
| `returnAll` | true/false, default `false` | Fetch every page: Fetches page after page until the connector runs out, and refuses past 10,000 rows rather than answering a short list. Set a sort first — paging without an order can repeat or miss rows. |
| `sort` | text, default `""` | Sort by field ids: Comma-separated AFAS Profit field ids; put a minus in front of one to sort it descending. |
| `filterFieldIds` | text, optional | Filter field ids: Comma-separated AFAS Profit field ids — one per filter, in the same order as below. |
| `filterValues` | text, optional | Filter values: One value per field id above, in the same order. |
| `operatorTypes` | text, optional | Filter operators: AFAS Profit operator type ids, comma-separated — one per field id above, in the same order. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `rows` array — Rows · `rowCount` number — Row count · `hasMore` boolean — More rows are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"afas.get","version":3,"label":"Get records","config":{"take":100,"skip":0,"returnAll":false,"sort":"","cursor":"","connection":"<connection id>","connector":"<getconnector>"}}
```

### Older version `afas.get@1` — still runs; upgrade it to @3

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `connector` | text, required | GetConnector — a value from the vendor's own list |
| `take` | number, default `100` | Max rows: Rows per call, 1 to 1,000. With Fetch every page on, this is the page size. |
| `skip` | number, default `0` | Skip rows: How many rows to pass over first — pairs with Max rows to read page by page. |
| `returnAll` | true/false, default `false` | Fetch every page: Fetches page after page until the connector runs out, and refuses past 10,000 rows rather than answering a short list. Set a sort first — paging without an order can repeat or miss rows. |
| `sort` | text, default `""` | Sort by field ids: Comma-separated AFAS Profit field ids; put a minus in front of one to sort it descending. |
| `filterFieldIds` | text, optional | Filter field ids: Comma-separated AFAS Profit field ids — one per filter, in the same order as below. |
| `filterValues` | text, optional | Filter values: One value per field id above, in the same order. |
| `operatorTypes` | text, optional | Filter operators: AFAS Profit operator type ids, comma-separated — one per field id above, in the same order. |

Upgrade @1 → @3: set `version` to 3 and replace the config — before, then after:
```json
{"take":100,"skip":0,"returnAll":false,"sort":"","connection":"<connection id>","connector":"<getconnector>"}
{"take":100,"skip":0,"returnAll":false,"sort":"","cursor":"","connection":"<connection id>","connector":"<getconnector>"}
```

### Older version `afas.get@2` — still runs; upgrade it to @3

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `connector` | text, required | GetConnector — a value from the vendor's own list |
| `take` | number, default `100` | Max rows: Rows per call, 1 to 1,000 — or -1 with Skip rows -1 for every row in one call. With Fetch every page on, this is the page size. |
| `skip` | number, default `0` | Skip rows: How many rows to pass over first — pairs with Max rows to read page by page. Set both to -1 for every row in one call. |
| `returnAll` | true/false, default `false` | Fetch every page: Fetches page after page until the connector runs out, and refuses past 10,000 rows rather than answering a short list. Set a sort first — paging without an order can repeat or miss rows. |
| `sort` | text, default `""` | Sort by field ids: Comma-separated AFAS Profit field ids; put a minus in front of one to sort it descending. |
| `filterFieldIds` | text, optional | Filter field ids: Comma-separated AFAS Profit field ids — one per filter, in the same order as below. |
| `filterValues` | text, optional | Filter values: One value per field id above, in the same order. |
| `operatorTypes` | text, optional | Filter operators: AFAS Profit operator type ids, comma-separated — one per field id above, in the same order. |

Upgrade @2 → @3: set `version` to 3 and replace the config — before, then after:
```json
{"take":100,"skip":0,"returnAll":false,"sort":"","connection":"<connection id>","connector":"<getconnector>"}
{"take":100,"skip":0,"returnAll":false,"sort":"","cursor":"","connection":"<connection id>","connector":"<getconnector>"}
```

## `afas.getSubject@1` — AFAS Profit — Get dossier item

Fetches one AFAS Profit dossier item and its attachments.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `subjectId` | text, required | Dossier item id: The DossierItemID — a GetConnector over dossier items hands it to you. |
| `fileId` | text, default `""` | File id: Optional: one attachment's GUID, when you want that file rather than the item. |
| `responseId` | text, default `""` | Response id: Refused in this version: AFAS names a ReactieId form for attachments on responses but does not publish its URL shape, and Flowmage does not guess at one. |

Output: `subject` any — Dossier item or file.

```json
{"type":"afas.getSubject","version":1,"label":"Get dossier item","config":{"fileId":"","responseId":"","connection":"<connection id>","subjectId":"<dossier item id>"}}
```

## `afas.insert@1` — AFAS Profit — Create record

Adds a new record to AFAS Profit through one of its UpdateConnectors.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `connector` | text, required | UpdateConnector: The node sends it a POST — AFAS's verb for adding a new record. — a value from the vendor's own list |
| `data` | any JSON, optional | Body: The AFAS Profit UpdateConnector envelope as JSON — or one {{ }} expression that produces it, e.g. {{ node["Build body"] }}. |

Output: `response` object — AFAS Profit response body.

```json
{"type":"afas.insert","version":1,"label":"Create record","config":{"connection":"<connection id>","connector":"<updateconnector>"}}
```

## `afas.listConnectors@1` — AFAS Profit — List connectors

Lists the GetConnectors and UpdateConnectors this connection may use.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |

Output: `getConnectors` array — GetConnectors · `updateConnectors` array — UpdateConnectors · `appName` string — App name · `environmentId` string — Environment id · `tokenExpiry` string — Token expiry.

```json
{"type":"afas.listConnectors","version":1,"label":"List connectors","config":{"connection":"<connection id>"}}
```

## `afas.runReport@1` — AFAS Profit — Run report

Runs an AFAS Profit report and returns its result.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `reportId` | text, required | Report GUID: The report's GUID as AFAS Profit shows it in the report's own settings. |
| `filterFieldIds` | text, optional | Filter field ids: Comma-separated field ids — one per filter, in the same order as below. |
| `filterValues` | text, optional | Filter values: One value per field id above, in the same order. |
| `operatorTypes` | text, optional | Filter operators: AFAS Profit operator type ids, comma-separated — one per field id above. |

Output: `report` any — Report result.

```json
{"type":"afas.runReport","version":1,"label":"Run report","config":{"connection":"<connection id>","reportId":"<report guid>"}}
```

## `afas.update@1` — AFAS Profit — Update record

Changes existing data in AFAS Profit through an UpdateConnector.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `connector` | text, required | UpdateConnector: The node sends it a PUT — AFAS's verb for changing a record that already exists. — a value from the vendor's own list |
| `data` | any JSON, optional | Body: The AFAS Profit UpdateConnector envelope as JSON — or one {{ }} expression that produces it, e.g. {{ node["Build body"] }}. |

Output: `response` object — AFAS Profit response body.

```json
{"type":"afas.update","version":1,"label":"Update record","config":{"connection":"<connection id>","connector":"<updateconnector>"}}
```

## `afas.uploadFile@1` — AFAS Profit — Upload a file

Attaches a file to AFAS Profit through an UpdateConnector.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `afas-profit`) |
| `connector` | text, default `"KnSubject"` | UpdateConnector: The UpdateConnector the file is attached through, e.g. KnSubject. — a value from the vendor's own list |
| `fileName` | text, required | File name: The file's name, extension included. Pick the file, then paste its base64 below. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("invoice.pdf").base64 }}. Multipart uploads over the "Use fixed IP address" lane are unverified; leave that toggle off for this node. — its `{{ }}` must give a string |

Output: `response` object — AFAS Profit response body.

```json
{"type":"afas.uploadFile","version":1,"label":"Upload a file","config":{"connector":"KnSubject","connection":"<connection id>","fileName":"<file name>","contentBase64":"<file contents>"}}
```


---

## File: references/nodes/asana.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Asana nodes (`asana.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `asana.addComment@1` — Asana — Add comment

Writes a comment on an Asana task, as plain text or rich text.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `taskId` | text, required | Task id |
| `text` | text, required | Comment: Rich text accepts <a>, <strong>, <em> and <ul>/<li>. Values from earlier nodes are escaped for you. |
| `format` | "text" or "html", default `"text"` | Format |

Output: `comment` object — Comment · `comment.gid` string — Id · `comment.created_at` string — Written at.

```json
{"type":"asana.addComment","version":1,"label":"Add comment","config":{"format":"text","connection":"<connection id>","taskId":"<task id>","text":"<comment>"}}
```

## `asana.addTaskToProject@1` — Asana — Add task to project

Adds an existing Asana task to another project.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `taskId` | text, required | Task id |
| `projectId` | text, required | Project id: The project to add the task to. |

Output: `added` boolean — Added.

```json
{"type":"asana.addTaskToProject","version":1,"label":"Add task to project","config":{"connection":"<connection id>","taskId":"<task id>","projectId":"<project id>"}}
```

## `asana.completeTask@1` — Asana — Complete task

Marks an Asana task as completed, or reopens it.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `taskId` | text, required | Task id |
| `completed` | true/false, default `true` | Completed: On marks the task done; off reopens it. |

Output: `task` object — Task · `task.gid` string — Id · `task.completed` boolean — Completed.

```json
{"type":"asana.completeTask","version":1,"label":"Complete task","config":{"completed":true,"connection":"<connection id>","taskId":"<task id>"}}
```

## `asana.createTask@1` — Asana — Create task

Creates a task in Asana, in a project and section you choose.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `name` | text, required | Task name |
| `projectId` | text, default `""` | Project id: The project's id — the List projects node gives you one. |
| `sectionId` | text, default `""` | Section id: The column the task lands in — the List sections node gives you one. — used when `projectId` is not "" |
| `workspaceId` | text, default `""` | Workspace id: Only needed when the task is not going into a project. — used when `projectId` is "" |
| `assignee` | text, default `""` | Assignee: A user's id or e-mail address, or the word me. |
| `dueOn` | text, default `""` | Due on |
| `dueAt` | text, default `""` | Due at |
| `descriptionFormat` | "text" or "html", default `"text"` | Description format |
| `description` | text, default `""` | Description: Rich text accepts <a>, <strong>, <em> and <ul>/<li>; Asana rejects <br>. Values from earlier nodes are escaped for you. |
| `customFields` | object of text, default `{}` | Custom fields: Asana keys custom fields by their gid, not by their name — the List custom fields node gives you the gids and the option gids. |
| `followers` | text, default `""` | Followers: Comma-separated user ids or e-mail addresses. |
| `parentTaskId` | text, default `""` | Parent task id |

Output: `taskId` string — Task id · `task` object — Task · `permalinkUrl` string — Link to the task.

```json
{"type":"asana.createTask","version":1,"label":"Create task","config":{"projectId":"","sectionId":"","workspaceId":"","assignee":"","dueOn":"","dueAt":"","descriptionFormat":"text","description":"","customFields":{},"followers":"","parentTaskId":"","connection":"<connection id>","name":"<task name>"}}
```

## `asana.deleteTask@1` — Asana — Delete task

Moves an Asana task to the trash, where it stays recoverable for 30 days.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `taskId` | text, required | Task id |

Output: `deleted` boolean — Deleted.

```json
{"type":"asana.deleteTask","version":1,"label":"Delete task","config":{"connection":"<connection id>","taskId":"<task id>"}}
```

## `asana.getProject@1` — Asana — Get project

Reads one Asana project with its notes and members.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `projectId` | text, required | Project id |
| `fields` | text, default `"gid,name,archived,notes,members,team,workspace,permalink_url"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |

Output: `project` object — Project · `project.gid` string — Id · `project.name` string — Name.

```json
{"type":"asana.getProject","version":1,"label":"Get project","config":{"fields":"gid,name,archived,notes,members,team,workspace,permalink_url","connection":"<connection id>","projectId":"<project id>"}}
```

## `asana.getTask@1` — Asana — Get task

Reads one Asana task with the fields you ask for.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `taskId` | text, required | Task id: The task's Asana id. A task's id is a string, not a number. |
| `fields` | text, default `"gid,name,completed,assignee,due_on,notes,projects"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |

Output: `task` object — Task · `task.gid` string — Id · `task.name` string — Name · `task.completed` boolean — Completed.

```json
{"type":"asana.getTask","version":1,"label":"Get task","config":{"fields":"gid,name,completed,assignee,due_on,notes,projects","connection":"<connection id>","taskId":"<task id>"}}
```

## `asana.getTeam@1` — Asana — Get team

Reads one Asana team.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `teamId` | text, required | Team id: The team's Asana id. List projects shows the team on each project it returns. |
| `fields` | text, default `"gid,name,description,organization"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |

Output: `team` object — Team · `team.gid` string — Id · `team.name` string — Name.

```json
{"type":"asana.getTeam","version":1,"label":"Get team","config":{"fields":"gid,name,description,organization","connection":"<connection id>","teamId":"<team id>"}}
```

## `asana.listAttachments@1` — Asana — List attachments

Lists the files attached to an Asana task or project.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `parentId` | text, required | Task or project id: The task, project or project brief whose attachments you want. |
| `limit` | number, default `100` | Files per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Asana's next-page cursor, and refuses past 5,000 attachments. |
| `fields` | text, default `"gid,name,download_url,host,created_at,size"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |

Output: `attachments` array — Attachments · `attachmentCount` number — Files in this answer.

```json
{"type":"asana.listAttachments","version":1,"label":"List attachments","config":{"limit":100,"returnAll":false,"fields":"gid,name,download_url,host,created_at,size","connection":"<connection id>","parentId":"<task or project id>"}}
```

## `asana.listComments@1` — Asana — List comments

Reads the comments and activity on an Asana task.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `taskId` | text, required | Task id |
| `limit` | number, default `100` | Stories per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Asana's next-page cursor, and refuses past 5,000 stories. |
| `fields` | text, default `"gid,created_at,created_by,text,html_text,type"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |

Output: `comments` array — Comments and activity · `commentCount` number — Stories in this answer.

```json
{"type":"asana.listComments","version":1,"label":"List comments","config":{"limit":100,"returnAll":false,"fields":"gid,created_at,created_by,text,html_text,type","connection":"<connection id>","taskId":"<task id>"}}
```

## `asana.listCustomFields@1` — Asana — List custom fields

Lists an Asana project's custom fields and the values they accept.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `projectId` | text, required | Project id |
| `limit` | number, default `100` | Fields per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Asana's next-page cursor, and refuses past 5,000 fields. |
| `fields` | text, default `"gid,custom_field.gid,custom_field.name,custom_field.resource_subtype,custom_field.enum_options.gid,custom_field.enum_options.name"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |

Output: `customFields` array — Custom field settings · `customFieldCount` number — Fields in this answer.

```json
{"type":"asana.listCustomFields","version":1,"label":"List custom fields","config":{"limit":100,"returnAll":false,"fields":"gid,custom_field.gid,custom_field.name,custom_field.resource_subtype,custom_field.enum_options.gid,custom_field.enum_options.name","connection":"<connection id>","projectId":"<project id>"}}
```

## `asana.listProjects@1` — Asana — List projects

Lists the Asana projects in a workspace or team.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `workspaceId` | text, default `""` | Workspace id: Projects in one workspace. Leave empty for every project you can see. |
| `teamId` | text, default `""` | Team id: Projects of one team — the Get team node gives you the id. |
| `archived` | true/false, optional | Archived |
| `limit` | number, default `100` | Projects per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Asana's next-page cursor, and refuses past 5,000 projects. |
| `fields` | text, default `"gid,name,archived,team,workspace"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |

Output: `projects` array — Projects · `projectCount` number — Projects in this answer.

```json
{"type":"asana.listProjects","version":1,"label":"List projects","config":{"workspaceId":"","teamId":"","limit":100,"returnAll":false,"fields":"gid,name,archived,team,workspace","connection":"<connection id>"}}
```

## `asana.listSections@1` — Asana — List sections

Lists the sections (columns) of an Asana project.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `projectId` | text, required | Project id |
| `limit` | number, default `100` | Sections per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Asana's next-page cursor, and refuses past 5,000 sections. |
| `fields` | text, default `"gid,name"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |

Output: `sections` array — Sections · `sectionCount` number — Sections in this answer.

```json
{"type":"asana.listSections","version":1,"label":"List sections","config":{"limit":100,"returnAll":false,"fields":"gid,name","connection":"<connection id>","projectId":"<project id>"}}
```

## `asana.listTasks@2` — Asana — List tasks

Lists Asana tasks in a project, section, tag or one person's list.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `projectId` | text, default `""` | Project id: Tasks in one project. The List projects node gives you the id. |
| `sectionId` | text, default `""` | Section id: Tasks in one section (column). The List sections node gives you the id. |
| `tagId` | text, default `""` | Tag id |
| `assignee` | text, default `""` | Assignee: One person's tasks — an id or e-mail, or the word me. Needs a Workspace id. |
| `workspaceId` | text, default `""` | Workspace id — used when `assignee` is not "" |
| `completedSince` | text, default `""` | Completed since |
| `limit` | number, default `100` | Tasks per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Asana's next-page cursor, and refuses past 5,000 tasks rather than answering a short list. |
| `fields` | text, default `"gid,name,completed,assignee,due_on"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `tasks` array — Tasks · `taskCount` number — Tasks in this answer · `hasMore` boolean — More tasks are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"asana.listTasks","version":2,"label":"List tasks","config":{"projectId":"","sectionId":"","tagId":"","assignee":"","workspaceId":"","completedSince":"","limit":100,"returnAll":false,"fields":"gid,name,completed,assignee,due_on","cursor":"","connection":"<connection id>"}}
```

### Older version `asana.listTasks@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `projectId` | text, default `""` | Project id: Tasks in one project. The List projects node gives you the id. |
| `sectionId` | text, default `""` | Section id: Tasks in one section (column). The List sections node gives you the id. |
| `tagId` | text, default `""` | Tag id |
| `assignee` | text, default `""` | Assignee: One person's tasks — an id or e-mail, or the word me. Needs a Workspace id. |
| `workspaceId` | text, default `""` | Workspace id — used when `assignee` is not "" |
| `completedSince` | text, default `""` | Completed since |
| `limit` | number, default `100` | Tasks per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Asana's next-page cursor, and refuses past 5,000 tasks rather than answering a short list. |
| `fields` | text, default `"gid,name,completed,assignee,due_on"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"projectId":"","sectionId":"","tagId":"","assignee":"","workspaceId":"","completedSince":"","limit":100,"returnAll":false,"fields":"gid,name,completed,assignee,due_on","connection":"<connection id>"}
{"projectId":"","sectionId":"","tagId":"","assignee":"","workspaceId":"","completedSince":"","limit":100,"returnAll":false,"fields":"gid,name,completed,assignee,due_on","cursor":"","connection":"<connection id>"}
```

## `asana.moveTaskToSection@1` — Asana — Move task to section

Moves an Asana task into a section of its project.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `taskId` | text, required | Task id |
| `sectionId` | text, required | Section id: The section (column) to move it to — the List sections node gives you the id. |

Output: `moved` boolean — Moved.

```json
{"type":"asana.moveTaskToSection","version":1,"label":"Move task to section","config":{"connection":"<connection id>","taskId":"<task id>","sectionId":"<section id>"}}
```

## `asana.searchTasks@1` — Asana — Search tasks

Searches a whole Asana workspace by text, assignee, project and dates.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `workspaceId` | text, required | Workspace id: The workspace to search — search is per workspace. |
| `text` | text, default `""` | Text |
| `assigneeAny` | text, default `""` | Assignee: One person's tasks — an id or e-mail, or the word me. |
| `projectIds` | text, default `""` | Projects: Comma-separated project ids to search inside. |
| `completed` | true/false, optional | Completed |
| `sortBy` | "due_date" or "created_at" or "completed_at" or "likes" or "modified_at", default `"modified_at"` | Sort by |
| `limit` | number, default `100` | Tasks per call: 1 to 100. Asana's search cannot be paged: this returns at most 100 tasks, the newest first — narrow the search if you need more. |
| `fields` | text, default `"gid,name,completed,assignee,due_on,projects"` | Fields to fetch: Asana leaves out anything you do not ask for. Add a field here to get it back. |

Output: `tasks` array — Tasks · `taskCount` number — Tasks in this answer.

```json
{"type":"asana.searchTasks","version":1,"label":"Search tasks","config":{"text":"","assigneeAny":"","projectIds":"","sortBy":"modified_at","limit":100,"fields":"gid,name,completed,assignee,due_on,projects","connection":"<connection id>","workspaceId":"<workspace id>"}}
```

## `asana.updateTask@1` — Asana — Update task

Changes an Asana task's name, description, assignee, due date or custom fields.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `taskId` | text, required | Task id |
| `name` | text, default `""` | Name |
| `descriptionFormat` | "text" or "html", default `"text"` | Description format |
| `description` | text, default `""` | Description: Leave empty to keep the description as it is. Rich text accepts <a>, <strong>, <em> and <ul>/<li>; values from earlier nodes are escaped for you. |
| `assignee` | text, default `""` | Assignee |
| `dueOn` | text, default `""` | Due on |
| `customFields` | object of text, default `{}` | Custom fields: Keyed by the custom field's gid — the List custom fields node gives you the gids. |

Output: `task` object — Task · `task.gid` string — Id · `task.name` string — Name.

```json
{"type":"asana.updateTask","version":1,"label":"Update task","config":{"name":"","descriptionFormat":"text","description":"","assignee":"","dueOn":"","customFields":{},"connection":"<connection id>","taskId":"<task id>"}}
```

## `asana.uploadAttachment@1` — Asana — Upload attachment

Attaches a file to an Asana task.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `asana`) |
| `parentId` | text, required | Task id: The task to attach the file to. |
| `name` | text, required | File name: The file's name, extension included. Pick the file, then paste its base64 below. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("name").base64 }}. Multipart uploads over the "Use fixed IP address" lane are unverified; leave that toggle off for this node. — its `{{ }}` must give a string |

Output: `attachment` object — Attachment · `attachment.gid` string — Id · `attachment.name` string — File name · `attachment.download_url` string — Download address.

```json
{"type":"asana.uploadAttachment","version":1,"label":"Upload attachment","config":{"connection":"<connection id>","parentId":"<task id>","name":"<file name>","contentBase64":"<file contents>"}}
```


---

## File: references/nodes/bullhorn.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Bullhorn nodes (`bullhorn.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `bullhorn.associate@1` — Bullhorn — Link records

Links records to a Bullhorn record's list field.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `entityId` | text, required | Record id |
| `associationField` | text, required | List field: The record's to-many field, e.g. categories or primarySkills. |
| `associationIds` | text, required | Records to link: One id, a comma-separated list, or a JSON array like [12, 13]. |

Output: `associatedIds` array — Linked record ids · `count` number — How many were linked.

```json
{"type":"bullhorn.associate","version":1,"label":"Link records","config":{"connection":"<connection id>","entityType":"<entity>","entityId":"<record id>","associationField":"<list field>","associationIds":"<records to link>"}}
```

## `bullhorn.createEntity@1` — Bullhorn — Create record

Creates a new record in Bullhorn.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `data` | any JSON, optional | Fields: The record's fields as JSON — or one {{ }} expression that produces the object. Bullhorn cannot create linked records in the same call. |

Output: `changedEntityId` number — New record id · `changedEntityType` string — Entity · `changeType` string — Change type.

```json
{"type":"bullhorn.createEntity","version":1,"label":"Create record","config":{"connection":"<connection id>","entityType":"<entity>"}}
```

## `bullhorn.deleteEntity@1` — Bullhorn — Delete record

Deletes a Bullhorn record, or marks it deleted.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity: BusinessSector, Category, Country, ClientCorporation, Skill, Specialty, State and TimeUnit cannot be deleted at all. |
| `entityId` | text, required | Record id |

Output: `changedEntityId` number — Record id · `changeType` string — Change type.

```json
{"type":"bullhorn.deleteEntity","version":1,"label":"Delete record","config":{"connection":"<connection id>","entityType":"<entity>","entityId":"<record id>"}}
```

## `bullhorn.deleteFile@1` — Bullhorn — Remove attachment

Removes a file from a Bullhorn record.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `entityId` | text, required | Record id |
| `fileId` | text, required | File id |

Output: `deleted` boolean — Deleted.

```json
{"type":"bullhorn.deleteFile","version":1,"label":"Remove attachment","config":{"connection":"<connection id>","entityType":"<entity>","entityId":"<record id>","fileId":"<file id>"}}
```

## `bullhorn.dissociate@1` — Bullhorn — Unlink records

Removes records from a Bullhorn record's list field.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `entityId` | text, required | Record id |
| `associationField` | text, required | List field |
| `associationIds` | text, required | Records to unlink: One id, a comma-separated list, or a JSON array like [12, 13]. |

Output: `dissociatedIds` array — Unlinked record ids · `count` number — How many were unlinked.

```json
{"type":"bullhorn.dissociate","version":1,"label":"Unlink records","config":{"connection":"<connection id>","entityType":"<entity>","entityId":"<record id>","associationField":"<list field>","associationIds":"<records to unlink>"}}
```

## `bullhorn.getEntity@1` — Bullhorn — Get record

Fetches one Bullhorn record by its id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity: The Bullhorn entity name, exactly as Bullhorn spells it — Candidate, ClientContact, JobOrder, Placement, Note. |
| `entityId` | text, required | Record id: One record id, or several separated by commas. |
| `fields` | text, required | Fields: The fields you want back, comma-separated. Bullhorn no longer accepts * here. |
| `meta` | "off" or "basic" or "full", default `"off"` | Meta |
| `showEditable` | true/false, default `false` | Show editable |

Output: `data` any — One record, or a list when you asked for several ids.

```json
{"type":"bullhorn.getEntity","version":1,"label":"Get record","config":{"meta":"off","showEditable":false,"connection":"<connection id>","entityType":"<entity>","entityId":"<record id>","fields":"<fields>"}}
```

## `bullhorn.getFile@1` — Bullhorn — Download attachment

Downloads one file attached to a Bullhorn record.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `entityId` | text, required | Record id |
| `fileId` | text, required | File id: The attachment's id — the List attachments node gives you files[].id. |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"bullhorn.getFile","version":1,"label":"Download attachment","config":{"connection":"<connection id>","entityType":"<entity>","entityId":"<record id>","fileId":"<file id>"}}
```

## `bullhorn.getMeta@1` — Bullhorn — Describe entity

Lists the fields and types Bullhorn holds for one entity.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity: The entity to describe — this is how you find the field names the other nodes need. |
| `detail` | "basic" or "full", default `"full"` | Detail |

Output: `entity` string — Entity · `fields` array — Fields · `label` string — Label.

```json
{"type":"bullhorn.getMeta","version":1,"label":"Describe entity","config":{"detail":"full","connection":"<connection id>","entityType":"<entity>"}}
```

## `bullhorn.listFiles@2` — Bullhorn — List attachments

Lists the files attached to a Bullhorn record.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `entityId` | text, required | Record id |
| `fields` | text, default `"id,name,description,contentType,contentSubType,fileType,fileExtension,fileSize,dateAdded,isPrivate"` | Fields |
| `count` | number, default `100` | Files per call: 1 to 500. |
| `start` | number, default `0` | Skip files |
| `returnAll` | true/false, default `false` | Fetch every page: Pages with start until a short page, and refuses past 10,000 files. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `files` array — Files · `fileCount` number — Files in this answer · `hasMore` boolean — More files are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"bullhorn.listFiles","version":2,"label":"List attachments","config":{"fields":"id,name,description,contentType,contentSubType,fileType,fileExtension,fileSize,dateAdded,isPrivate","count":100,"start":0,"returnAll":false,"cursor":"","connection":"<connection id>","entityType":"<entity>","entityId":"<record id>"}}
```

### Older version `bullhorn.listFiles@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `entityId` | text, required | Record id |
| `fields` | text, default `"id,name,description,contentType,contentSubType,fileType,fileExtension,fileSize,dateAdded,isPrivate"` | Fields |
| `count` | number, default `100` | Files per call: 1 to 500. |
| `start` | number, default `0` | Skip files |
| `returnAll` | true/false, default `false` | Fetch every page: Pages with start until a short page, and refuses past 10,000 files. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"fields":"id,name,description,contentType,contentSubType,fileType,fileExtension,fileSize,dateAdded,isPrivate","count":100,"start":0,"returnAll":false,"connection":"<connection id>","entityType":"<entity>","entityId":"<record id>"}
{"fields":"id,name,description,contentType,contentSubType,fileType,fileExtension,fileSize,dateAdded,isPrivate","count":100,"start":0,"returnAll":false,"cursor":"","connection":"<connection id>","entityType":"<entity>","entityId":"<record id>"}
```

## `bullhorn.query@2` — Bullhorn — Query records

Reads Bullhorn records with a database-style where clause.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `where` | text, required | Where: Bullhorn's own JPQL where clause, e.g. id > 0 AND isOpen = true. |
| `fields` | text, required | Fields: The fields you want back, comma-separated. Bullhorn no longer accepts * here. |
| `orderBy` | text, default `""` | Order by |
| `totalOnly` | true/false, default `false` | Total only |
| `count` | number, default `100` | Records per call: 1 to 500. |
| `start` | number, default `0` | Skip records |
| `returnAll` | true/false, default `false` | Fetch every page: Pages with start until a short page, and refuses past 10,000 records. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `records` array — Records · `recordCount` number — Records in this answer · `total` number — How many Bullhorn counted · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"bullhorn.query","version":2,"label":"Query records","config":{"orderBy":"","totalOnly":false,"count":100,"start":0,"returnAll":false,"cursor":"","connection":"<connection id>","entityType":"<entity>","where":"<where>","fields":"<fields>"}}
```

### Older version `bullhorn.query@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `where` | text, required | Where: Bullhorn's own JPQL where clause, e.g. id > 0 AND isOpen = true. |
| `fields` | text, required | Fields: The fields you want back, comma-separated. Bullhorn no longer accepts * here. |
| `orderBy` | text, default `""` | Order by |
| `totalOnly` | true/false, default `false` | Total only |
| `count` | number, default `100` | Records per call: 1 to 500. |
| `start` | number, default `0` | Skip records |
| `returnAll` | true/false, default `false` | Fetch every page: Pages with start until a short page, and refuses past 10,000 records. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"orderBy":"","totalOnly":false,"count":100,"start":0,"returnAll":false,"connection":"<connection id>","entityType":"<entity>","where":"<where>","fields":"<fields>"}
{"orderBy":"","totalOnly":false,"count":100,"start":0,"returnAll":false,"cursor":"","connection":"<connection id>","entityType":"<entity>","where":"<where>","fields":"<fields>"}
```

## `bullhorn.search@2` — Bullhorn — Search records

Searches Bullhorn's index with a Lucene query.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity: Candidate, ClientContact, ClientCorporation, JobOrder, Lead, Note, Opportunity, Placement, Task. |
| `query` | text, required | Lucene query: Bullhorn's own search syntax, e.g. isOpen:true AND isDeleted:false. |
| `fields` | text, required | Fields: The fields you want back, comma-separated. Bullhorn no longer accepts * here. |
| `sort` | text, default `""` | Sort by: A field name; put a minus in front to reverse it. |
| `count` | number, default `100` | Records per call: 1 to 500. With Fetch every page on, this is the page size. |
| `start` | number, default `0` | Skip records |
| `returnAll` | true/false, default `false` | Fetch every page: Pages with start until a short page, and refuses past 10,000 records rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `records` array — Records · `recordCount` number — Records in this answer · `total` number — How many Bullhorn found in total · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"bullhorn.search","version":2,"label":"Search records","config":{"sort":"","count":100,"start":0,"returnAll":false,"cursor":"","connection":"<connection id>","entityType":"<entity>","query":"<lucene query>","fields":"<fields>"}}
```

### Older version `bullhorn.search@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity: Candidate, ClientContact, ClientCorporation, JobOrder, Lead, Note, Opportunity, Placement, Task. |
| `query` | text, required | Lucene query: Bullhorn's own search syntax, e.g. isOpen:true AND isDeleted:false. |
| `fields` | text, required | Fields: The fields you want back, comma-separated. Bullhorn no longer accepts * here. |
| `sort` | text, default `""` | Sort by: A field name; put a minus in front to reverse it. |
| `count` | number, default `100` | Records per call: 1 to 500. With Fetch every page on, this is the page size. |
| `start` | number, default `0` | Skip records |
| `returnAll` | true/false, default `false` | Fetch every page: Pages with start until a short page, and refuses past 10,000 records rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"sort":"","count":100,"start":0,"returnAll":false,"connection":"<connection id>","entityType":"<entity>","query":"<lucene query>","fields":"<fields>"}
{"sort":"","count":100,"start":0,"returnAll":false,"cursor":"","connection":"<connection id>","entityType":"<entity>","query":"<lucene query>","fields":"<fields>"}
```

## `bullhorn.updateEntity@1` — Bullhorn — Update record

Changes fields on an existing Bullhorn record.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `entityId` | text, required | Record id |
| `data` | any JSON, optional | Fields: The fields to change as JSON — or one {{ }} expression that produces the object. |

Output: `changedEntityId` number — Record id · `changedEntityType` string — Entity · `changeType` string — Change type.

```json
{"type":"bullhorn.updateEntity","version":1,"label":"Update record","config":{"connection":"<connection id>","entityType":"<entity>","entityId":"<record id>"}}
```

## `bullhorn.updateFile@1` — Bullhorn — Update attachment

Replaces a file already attached to a Bullhorn record.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `entityId` | text, required | Record id |
| `fileId` | text, required | File id |
| `content` | text, required | File contents: Base64 of the file — {{ file("cv.pdf").base64 }}. — its `{{ }}` must give a string |
| `name` | text, required | File name |
| `fileType` | text, default `"SAMPLE"` | File type |
| `externalId` | text, required | External id |
| `contentType` | text, default `""` | Content type |
| `description` | text, default `""` | Description |
| `category` | text, default `""` | Attachment type: Bullhorn's own `type` field on the attachment. |

Output: `fileId` number — File id.

```json
{"type":"bullhorn.updateFile","version":1,"label":"Update attachment","config":{"fileType":"SAMPLE","contentType":"","description":"","category":"","connection":"<connection id>","entityType":"<entity>","entityId":"<record id>","fileId":"<file id>","content":"<file contents>","name":"<file name>","externalId":"<external id>"}}
```

## `bullhorn.uploadFile@1` — Bullhorn — Attach file

Attaches a file to a Bullhorn record.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `bullhorn`) |
| `entityType` | text, required | Entity |
| `entityId` | text, required | Record id |
| `content` | text, required | File contents: Base64 of the file — {{ file("cv.pdf").base64 }}. Bullhorn keeps the bytes and the name you give here. — its `{{ }}` must give a string |
| `name` | text, required | File name: The name Bullhorn stores, extension included. |
| `fileType` | text, default `"SAMPLE"` | File type |
| `externalId` | text, required | External id: Bullhorn's label for this attachment. It is not checked for duplicates. |
| `contentType` | text, default `""` | Content type |
| `description` | text, default `""` | Description |
| `category` | text, default `""` | Attachment type: Bullhorn's own `type` field on the attachment. |

Output: `fileId` number — File id.

```json
{"type":"bullhorn.uploadFile","version":1,"label":"Attach file","config":{"fileType":"SAMPLE","contentType":"","description":"","category":"","connection":"<connection id>","entityType":"<entity>","entityId":"<record id>","content":"<file contents>","name":"<file name>","externalId":"<external id>"}}
```


---

## File: references/nodes/carerix.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Carerix nodes (`carerix.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `carerix.applyCandidate@1` — Carerix — Register application

Registers someone's application on a published Carerix vacancy.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `publicationId` | text, default `""` | Publication id: The published vacancy the person applied to. Leave it empty for an open application, and fill in Business line instead. |
| `firstName` | text, default `""` | First name |
| `lastName` | text, default `""` | Last name |
| `emailAddress` | text, default `""` | E-mail address |
| `mobileNumber` | text, default `""` | Mobile number |
| `motivation` | text, default `""` | Motivation |
| `dedupe` | true/false, default `true` | Let Carerix merge duplicates: On — Carerix's own default for an apply — an existing candidate is merged into instead of duplicated. |
| `createMatch` | true/false, default `true` | Put them on the vacancy: On, Carerix creates the match as well as the candidate. Off, only the candidate is registered. |
| `sendConfirmationEmail` | true/false, default `true` | Let Carerix send its confirmation: On, Carerix sends its own confirmation e-mail from the web-confirm template. Turn it off if your flow sends its own. |
| `confirmMailTemplate` | text, default `""` | Confirmation template code: The code of the e-mail template to confirm with. Empty lets Carerix use its own web-confirm template. |
| `businessLineId` | text, default `""` | Business line id: Carerix's prod argument, for an open application that belongs to a business line rather than to one published vacancy. Which record this points at is not documented and has not been confirmed against a tenant. |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CREmployeeRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |

Output: `candidateId` string — Candidate id · `candidate` object — Candidate.

```json
{"type":"carerix.applyCandidate","version":1,"label":"Register application","config":{"publicationId":"","firstName":"","lastName":"","emailAddress":"","mobileNumber":"","motivation":"","dedupe":true,"createMatch":true,"sendConfirmationEmail":true,"confirmMailTemplate":"","businessLineId":"","extraFields":{},"language":"","connection":"<connection id>"}}
```

## `carerix.createActivity@1` — Carerix — Create activity

Logs a note, task, meeting or e-mail against a Carerix record.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `kind` | "activity" or "campaign" or "edition" or "email" or "meeting" or "newsLetter" or "note" or "system" or "task", default `"note"` | Kind: Which flavour of Carerix activity to log. They are one record type with a kind. |
| `title` | text, default `""` | Title |
| `subject` | text, default `""` | Subject |
| `notes` | text, default `""` | Notes |
| `startDate` | text, default `""` | Starts: Carerix dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, never ISO-8601. |
| `endDate` | text, default `""` | Ends |
| `deadline` | text, default `""` | Deadline |
| `priority` | text, default `""` | Priority |
| `status` | text, default `""` | Status |
| `location` | text, default `""` | Location |
| `candidateId` | text, default `""` | Candidate id: Fill in at least one of Candidate, Match, Vacancy, Company or Contact — an activity attached to nothing is invisible in Carerix. |
| `matchId` | text, default `""` | Match id |
| `vacancyId` | text, default `""` | Vacancy id |
| `companyId` | text, default `""` | Company id |
| `contactId` | text, default `""` | Contact id |
| `publicationId` | text, default `""` | Publication id |
| `ownerUserId` | text, default `""` | Owner id |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CRToDoRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |

Output: `activityId` string — New activity id · `activity` object — Activity.

```json
{"type":"carerix.createActivity","version":1,"label":"Create activity","config":{"kind":"note","title":"","subject":"","notes":"","startDate":"","endDate":"","deadline":"","priority":"","status":"","location":"","candidateId":"","matchId":"","vacancyId":"","companyId":"","contactId":"","publicationId":"","ownerUserId":"","extraFields":{},"connection":"<connection id>"}}
```

## `carerix.createCandidate@1` — Carerix — Create candidate

Adds a candidate to Carerix, optionally merging duplicates.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `firstName` | text, default `""` | First name |
| `lastName` | text, default `""` | Last name |
| `lastNamePrefix` | text, default `""` | Last-name prefix: The Dutch tussenvoegsel — van, de, van der. |
| `emailAddress` | text, default `""` | E-mail address |
| `mobileNumber` | text, default `""` | Mobile number |
| `phoneNumber` | text, default `""` | Phone number |
| `street` | text, default `""` | Street |
| `houseNumber` | text, default `""` | House number |
| `postalCode` | text, default `""` | Postal code |
| `city` | text, default `""` | City |
| `birthDate` | text, default `""` | Date of birth: Carerix dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, never ISO-8601. |
| `availableDate` | text, default `""` | Available from |
| `notes` | text, default `""` | Notes |
| `dedupe` | true/false, default `false` | Let Carerix merge duplicates: On, Carerix looks for an existing candidate and merges into it instead of adding a second one. It is the vendor's own matcher, so it is a good bet and not a promise. |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CREmployeeRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |

Output: `candidateId` string — New candidate id · `candidate` object — Candidate.

```json
{"type":"carerix.createCandidate","version":1,"label":"Create candidate","config":{"firstName":"","lastName":"","lastNamePrefix":"","emailAddress":"","mobileNumber":"","phoneNumber":"","street":"","houseNumber":"","postalCode":"","city":"","birthDate":"","availableDate":"","notes":"","dedupe":false,"extraFields":{},"language":"","connection":"<connection id>"}}
```

## `carerix.createCompany@1` — Carerix — Create company

Adds a client company to Carerix.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `name` | text, default `""` | Company name |
| `shortName` | text, default `""` | Short name: What Carerix shows in narrow lists. |
| `visitStreet` | text, default `""` | Street |
| `visitNumber` | text, default `""` | House number |
| `visitPostalCode` | text, default `""` | Postal code |
| `visitCity` | text, default `""` | City |
| `phoneNumber` | text, default `""` | Phone number |
| `emailAddress` | text, default `""` | E-mail address |
| `url` | text, default `""` | Website |
| `kvkNumber` | text, default `""` | Chamber of Commerce number: The Dutch KvK number. |
| `debtorNumber` | text, default `""` | Debtor number: The number your accounting package knows this client by. |
| `ownerUserId` | text, default `""` | Owner id: The Carerix user who owns the relationship. The List users node answers one. |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CRCompanyRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |

Output: `companyId` string — New company id · `company` object — Company.

```json
{"type":"carerix.createCompany","version":1,"label":"Create company","config":{"name":"","shortName":"","visitStreet":"","visitNumber":"","visitPostalCode":"","visitCity":"","phoneNumber":"","emailAddress":"","url":"","kvkNumber":"","debtorNumber":"","ownerUserId":"","extraFields":{},"connection":"<connection id>"}}
```

## `carerix.createContact@1` — Carerix — Create contact

Adds a contact person at one of your Carerix client companies.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `firstName` | text, default `""` | First name |
| `lastName` | text, default `""` | Last name |
| `lastNamePrefix` | text, default `""` | Last-name prefix |
| `emailAddress` | text, default `""` | E-mail address |
| `phoneNumberBusiness` | text, default `""` | Business phone |
| `mobileNumber` | text, default `""` | Mobile number |
| `jobTitle` | text, default `""` | Job title |
| `companyId` | text, default `""` | Company id: The client company this person works at. The List companies node answers one. |
| `dedupe` | true/false, default `false` | Let Carerix merge duplicates: On, Carerix looks for an existing contact and merges into it instead of adding a second one. |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CRUserRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |

Output: `contactId` string — New contact id · `contact` object — Contact.

```json
{"type":"carerix.createContact","version":1,"label":"Create contact","config":{"firstName":"","lastName":"","lastNamePrefix":"","emailAddress":"","phoneNumberBusiness":"","mobileNumber":"","jobTitle":"","companyId":"","dedupe":false,"extraFields":{},"language":"","connection":"<connection id>"}}
```

## `carerix.createMatch@1` — Carerix — Create match

Puts a Carerix candidate forward on a vacancy.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `candidateId` | text, default `""` | Candidate id: The candidate being put forward. The List candidates node answers one. |
| `vacancyId` | text, default `""` | Vacancy id: The vacancy to put them on. The List vacancies node answers one. |
| `publicationId` | text, default `""` | Publication id: The published vacancy they answered, when the match comes from an advert. |
| `ownerUserId` | text, default `""` | Owner id |
| `motivation` | text, default `""` | Motivation |
| `notes` | text, default `""` | Notes |
| `jobStartDate` | text, default `""` | Job starts: Carerix dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, never ISO-8601. |
| `applySource` | text, default `""` | Source: Where the application came from — the utm_source of a career site. |
| `applyMedium` | text, default `""` | Medium |
| `applyCampaign` | text, default `""` | Campaign |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CRMatchRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |

Output: `matchId` string — New match id · `match` object — Match.

```json
{"type":"carerix.createMatch","version":1,"label":"Create match","config":{"candidateId":"","vacancyId":"","publicationId":"","ownerUserId":"","motivation":"","notes":"","jobStartDate":"","applySource":"","applyMedium":"","applyCampaign":"","extraFields":{},"connection":"<connection id>"}}
```

## `carerix.createVacancy@1` — Carerix — Create vacancy

Opens a new vacancy in Carerix for one of your client companies.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `jobTitle` | text, default `""` | Job title |
| `companyId` | text, default `""` | Company id: The client company this vacancy belongs to. The List companies node answers one. |
| `ownerUserId` | text, default `""` | Owner id: The Carerix user who owns the vacancy. The List users node answers one. |
| `vacancyNo` | text, default `""` | Vacancy number: Your own reference. Leave it empty and Carerix numbers the vacancy itself. |
| `startDate` | text, default `""` | Starts: Carerix dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, never ISO-8601. |
| `endDate` | text, default `""` | Ends |
| `deadline` | text, default `""` | Deadline |
| `numberOfVacancies` | number, default `1` | Places |
| `hoursPerWeek` | text, default `""` | Hours a week |
| `workCity` | text, default `""` | Work city |
| `requirements` | text, default `""` | Requirements |
| `offerInformation` | text, default `""` | What you offer |
| `introInformation` | text, default `""` | Introduction |
| `vacancyInformation` | text, default `""` | The vacancy text |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CRVacancyRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |

Output: `vacancyId` string — New vacancy id · `vacancy` object — Vacancy.

```json
{"type":"carerix.createVacancy","version":1,"label":"Create vacancy","config":{"jobTitle":"","companyId":"","ownerUserId":"","vacancyNo":"","startDate":"","endDate":"","deadline":"","numberOfVacancies":1,"hoursPerWeek":"","workCity":"","requirements":"","offerInformation":"","introInformation":"","vacancyInformation":"","extraFields":{},"connection":"<connection id>"}}
```

## `carerix.downloadAttachment@1` — Carerix — Download attachment

Fetches one Carerix document's contents as base64.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `attachmentId` | text, default `""` | Attachment id: The _id of the document. The List attachments node answers one. A large file arrives about a third bigger than it is on disk, because base64 is wider than bytes — a run whose parse limit is smaller than that refuses it. |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes · `attachment` object — The attachment record.

```json
{"type":"carerix.downloadAttachment","version":1,"label":"Download attachment","config":{"attachmentId":"","connection":"<connection id>"}}
```

## `carerix.getCandidate@1` — Carerix — Get candidate

Fetches one Carerix candidate by its id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `candidateId` | text, default `""` | Candidate id: The _id of the candidate. The List candidates node answers one. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind firstName lastName lastNamePrefix emailAddress mobileNumber phoneNumber street houseNumber postalCode city country birthDate availableDate notes creationDate modificationDate toStatusNode { _id label } owner { _id firstName lastName }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. A candidate has several hundred fields; docs.carerix.io/graphql lists them all. |

Output: `candidate` object — Candidate.

```json
{"type":"carerix.getCandidate","version":1,"label":"Get candidate","config":{"candidateId":"","language":"","fields":"_id _kind firstName lastName lastNamePrefix emailAddress mobileNumber phoneNumber street houseNumber postalCode city country birthDate availableDate notes creationDate modificationDate toStatusNode { _id label } owner { _id firstName lastName }","connection":"<connection id>"}}
```

## `carerix.getVacancy@1` — Carerix — Get vacancy

Fetches one Carerix vacancy by its id, with its full text.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `vacancyId` | text, default `""` | Vacancy id: The _id of the vacancy. The List vacancies node answers one. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind vacancyNo jobTitle companyName startDate endDate deadline numberOfVacancies hoursPerWeek workCity requirements offerInformation introInformation vacancyInformation isConfidential creationDate modificationDate toCompany { _id name } owner { _id firstName lastName } toStatusNode { _id label }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. docs.carerix.io/graphql lists every field a vacancy has. |

Output: `vacancy` object — Vacancy.

```json
{"type":"carerix.getVacancy","version":1,"label":"Get vacancy","config":{"vacancyId":"","language":"","fields":"_id _kind vacancyNo jobTitle companyName startDate endDate deadline numberOfVacancies hoursPerWeek workCity requirements offerInformation introInformation vacancyInformation isConfidential creationDate modificationDate toCompany { _id name } owner { _id firstName lastName } toStatusNode { _id label }","connection":"<connection id>"}}
```

## `carerix.listActivities@2` — Carerix — List activities

Lists Carerix notes, tasks, e-mails, meetings or campaigns.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 activities rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Activities per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind title subject startDate endDate deadline status priority isAllDay location creationDate modificationDate owner { _id firstName lastName } toEmployee { _id firstName lastName } toMatch { _id } toVacancy { _id jobTitle } toCompany { _id name } toActivityTypeNode { _id label } toResultNode { _id label }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |
| `kind` | "activity" or "campaign" or "edition" or "email" or "meeting" or "newsLetter" or "note" or "system" or "task", default `"note"` | Kind: Which flavour of Carerix activity to list. They are one record type with a kind, so a flow that wants two kinds reads twice. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `activities` array — Activities · `activityCount` number — Activities in this answer · `hasMore` boolean — More pages are waiting behind this one · `totalElements` number — Records Carerix says match · `page` number — The page this answer came from · `nextPage` number — The next page number — absent on the last page · `nextCursor` string — Cursor for the next page.

```json
{"type":"carerix.listActivities","version":2,"label":"List activities","config":{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind title subject startDate endDate deadline status priority isAllDay location creationDate modificationDate owner { _id firstName lastName } toEmployee { _id firstName lastName } toMatch { _id } toVacancy { _id jobTitle } toCompany { _id name } toActivityTypeNode { _id label } toResultNode { _id label }","kind":"note","cursor":"","connection":"<connection id>"}}
```

### Older version `carerix.listActivities@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 activities rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Activities per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind title subject startDate endDate deadline status priority isAllDay location creationDate modificationDate owner { _id firstName lastName } toEmployee { _id firstName lastName } toMatch { _id } toVacancy { _id jobTitle } toCompany { _id name } toActivityTypeNode { _id label } toResultNode { _id label }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |
| `kind` | "activity" or "campaign" or "edition" or "email" or "meeting" or "newsLetter" or "note" or "system" or "task", default `"note"` | Kind: Which flavour of Carerix activity to list. They are one record type with a kind, so a flow that wants two kinds reads twice. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind title subject startDate endDate deadline status priority isAllDay location creationDate modificationDate owner { _id firstName lastName } toEmployee { _id firstName lastName } toMatch { _id } toVacancy { _id jobTitle } toCompany { _id name } toActivityTypeNode { _id label } toResultNode { _id label }","kind":"note","connection":"<connection id>"}
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind title subject startDate endDate deadline status priority isAllDay location creationDate modificationDate owner { _id firstName lastName } toEmployee { _id firstName lastName } toMatch { _id } toVacancy { _id jobTitle } toCompany { _id name } toActivityTypeNode { _id label } toResultNode { _id label }","kind":"note","cursor":"","connection":"<connection id>"}
```

## `carerix.listAttachments@2` — Carerix — List attachments

Lists Carerix documents and CVs, without their contents.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 attachments rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Attachments per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind label filePath downloadName attachmentMimeType attachmentSize size creationDate modificationDate entityKey toTypeNode { _id label } toStatusNode { _id label } owner { _id firstName lastName }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. Do not add content here: it is the whole file as base64, and a page of a hundred would be far past the run's parse limit. Use the Download attachment node for one document's bytes. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `attachments` array — Attachments · `attachmentCount` number — Attachments in this answer · `hasMore` boolean — More pages are waiting behind this one · `totalElements` number — Records Carerix says match · `page` number — The page this answer came from · `nextPage` number — The next page number — absent on the last page · `nextCursor` string — Cursor for the next page.

```json
{"type":"carerix.listAttachments","version":2,"label":"List attachments","config":{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind label filePath downloadName attachmentMimeType attachmentSize size creationDate modificationDate entityKey toTypeNode { _id label } toStatusNode { _id label } owner { _id firstName lastName }","cursor":"","connection":"<connection id>"}}
```

### Older version `carerix.listAttachments@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 attachments rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Attachments per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind label filePath downloadName attachmentMimeType attachmentSize size creationDate modificationDate entityKey toTypeNode { _id label } toStatusNode { _id label } owner { _id firstName lastName }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. Do not add content here: it is the whole file as base64, and a page of a hundred would be far past the run's parse limit. Use the Download attachment node for one document's bytes. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind label filePath downloadName attachmentMimeType attachmentSize size creationDate modificationDate entityKey toTypeNode { _id label } toStatusNode { _id label } owner { _id firstName lastName }","connection":"<connection id>"}
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind label filePath downloadName attachmentMimeType attachmentSize size creationDate modificationDate entityKey toTypeNode { _id label } toStatusNode { _id label } owner { _id firstName lastName }","cursor":"","connection":"<connection id>"}
```

## `carerix.listCandidates@2` — Carerix — List candidates

Lists Carerix candidates, filtered by a Carerix qualifier.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 candidates rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Candidates per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind firstName lastName lastNamePrefix emailAddress mobileNumber city postalCode creationDate modificationDate"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `candidates` array — Candidates · `candidateCount` number — Candidates in this answer · `hasMore` boolean — More pages are waiting behind this one · `totalElements` number — Records Carerix says match · `page` number — The page this answer came from · `nextPage` number — The next page number — absent on the last page · `nextCursor` string — Cursor for the next page.

```json
{"type":"carerix.listCandidates","version":2,"label":"List candidates","config":{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind firstName lastName lastNamePrefix emailAddress mobileNumber city postalCode creationDate modificationDate","cursor":"","connection":"<connection id>"}}
```

### Older version `carerix.listCandidates@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 candidates rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Candidates per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind firstName lastName lastNamePrefix emailAddress mobileNumber city postalCode creationDate modificationDate"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind firstName lastName lastNamePrefix emailAddress mobileNumber city postalCode creationDate modificationDate","connection":"<connection id>"}
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind firstName lastName lastNamePrefix emailAddress mobileNumber city postalCode creationDate modificationDate","cursor":"","connection":"<connection id>"}
```

## `carerix.listCompanies@2` — Carerix — List companies

Finds your Carerix client companies, with their address and owner.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 companies rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Companies per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind name shortName visitCity visitPostalCode visitStreet phoneNumber emailAddress url debtorNumber creationDate modificationDate toStatusNode { _id label } owner { _id firstName lastName }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `companies` array — Companies · `companyCount` number — Companies in this answer · `hasMore` boolean — More pages are waiting behind this one · `totalElements` number — Records Carerix says match · `page` number — The page this answer came from · `nextPage` number — The next page number — absent on the last page · `nextCursor` string — Cursor for the next page.

```json
{"type":"carerix.listCompanies","version":2,"label":"List companies","config":{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind name shortName visitCity visitPostalCode visitStreet phoneNumber emailAddress url debtorNumber creationDate modificationDate toStatusNode { _id label } owner { _id firstName lastName }","cursor":"","connection":"<connection id>"}}
```

### Older version `carerix.listCompanies@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 companies rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Companies per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind name shortName visitCity visitPostalCode visitStreet phoneNumber emailAddress url debtorNumber creationDate modificationDate toStatusNode { _id label } owner { _id firstName lastName }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind name shortName visitCity visitPostalCode visitStreet phoneNumber emailAddress url debtorNumber creationDate modificationDate toStatusNode { _id label } owner { _id firstName lastName }","connection":"<connection id>"}
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind name shortName visitCity visitPostalCode visitStreet phoneNumber emailAddress url debtorNumber creationDate modificationDate toStatusNode { _id label } owner { _id firstName lastName }","cursor":"","connection":"<connection id>"}
```

## `carerix.listContacts@2` — Carerix — List contacts

Lists the people at your Carerix client companies.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 contacts rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Contacts per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind firstName lastName lastNamePrefix emailAddress phoneNumberBusiness mobileNumber jobTitle isActive creationDate modificationDate toCompany { _id name }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `contacts` array — Contacts · `contactCount` number — Contacts in this answer · `hasMore` boolean — More pages are waiting behind this one · `totalElements` number — Records Carerix says match · `page` number — The page this answer came from · `nextPage` number — The next page number — absent on the last page · `nextCursor` string — Cursor for the next page.

```json
{"type":"carerix.listContacts","version":2,"label":"List contacts","config":{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind firstName lastName lastNamePrefix emailAddress phoneNumberBusiness mobileNumber jobTitle isActive creationDate modificationDate toCompany { _id name }","cursor":"","connection":"<connection id>"}}
```

### Older version `carerix.listContacts@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 contacts rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Contacts per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind firstName lastName lastNamePrefix emailAddress phoneNumberBusiness mobileNumber jobTitle isActive creationDate modificationDate toCompany { _id name }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind firstName lastName lastNamePrefix emailAddress phoneNumberBusiness mobileNumber jobTitle isActive creationDate modificationDate toCompany { _id name }","connection":"<connection id>"}
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind firstName lastName lastNamePrefix emailAddress phoneNumberBusiness mobileNumber jobTitle isActive creationDate modificationDate toCompany { _id name }","cursor":"","connection":"<connection id>"}
```

## `carerix.listMatches@2` — Carerix — List matches

Lists Carerix matches — a candidate on a vacancy, with its pipeline stage.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 matches rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Matches per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind creationDate modificationDate statusInfoModificationDate motivation notes jobStartDate fitScore applySource applyMedium toEmployee { _id firstName lastName emailAddress } toVacancy { _id vacancyNo jobTitle } toPublication { _id } statusInfo { _id } toUser { _id firstName lastName }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `matches` array — Matches · `matchCount` number — Matches in this answer · `hasMore` boolean — More pages are waiting behind this one · `totalElements` number — Records Carerix says match · `page` number — The page this answer came from · `nextPage` number — The next page number — absent on the last page · `nextCursor` string — Cursor for the next page.

```json
{"type":"carerix.listMatches","version":2,"label":"List matches","config":{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind creationDate modificationDate statusInfoModificationDate motivation notes jobStartDate fitScore applySource applyMedium toEmployee { _id firstName lastName emailAddress } toVacancy { _id vacancyNo jobTitle } toPublication { _id } statusInfo { _id } toUser { _id firstName lastName }","cursor":"","connection":"<connection id>"}}
```

### Older version `carerix.listMatches@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 matches rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Matches per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind creationDate modificationDate statusInfoModificationDate motivation notes jobStartDate fitScore applySource applyMedium toEmployee { _id firstName lastName emailAddress } toVacancy { _id vacancyNo jobTitle } toPublication { _id } statusInfo { _id } toUser { _id firstName lastName }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind creationDate modificationDate statusInfoModificationDate motivation notes jobStartDate fitScore applySource applyMedium toEmployee { _id firstName lastName emailAddress } toVacancy { _id vacancyNo jobTitle } toPublication { _id } statusInfo { _id } toUser { _id firstName lastName }","connection":"<connection id>"}
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind creationDate modificationDate statusInfoModificationDate motivation notes jobStartDate fitScore applySource applyMedium toEmployee { _id firstName lastName emailAddress } toVacancy { _id vacancyNo jobTitle } toPublication { _id } statusInfo { _id } toUser { _id firstName lastName }","cursor":"","connection":"<connection id>"}
```

## `carerix.listPublications@2` — Carerix — List publications

Lists Carerix publications — vacancies published to a job board or site.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 publications rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Publications per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind publicationStart publicationEnd status closed vacancyNo applyUrl vacancyUrl campaign workLocation creationDate modificationDate toVacancy { _id jobTitle } toMedium { _id } toCompany { _id name } toStatusNode { _id label }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `publications` array — Publications · `publicationCount` number — Publications in this answer · `hasMore` boolean — More pages are waiting behind this one · `totalElements` number — Records Carerix says match · `page` number — The page this answer came from · `nextPage` number — The next page number — absent on the last page · `nextCursor` string — Cursor for the next page.

```json
{"type":"carerix.listPublications","version":2,"label":"List publications","config":{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind publicationStart publicationEnd status closed vacancyNo applyUrl vacancyUrl campaign workLocation creationDate modificationDate toVacancy { _id jobTitle } toMedium { _id } toCompany { _id name } toStatusNode { _id label }","cursor":"","connection":"<connection id>"}}
```

### Older version `carerix.listPublications@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 publications rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Publications per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind publicationStart publicationEnd status closed vacancyNo applyUrl vacancyUrl campaign workLocation creationDate modificationDate toVacancy { _id jobTitle } toMedium { _id } toCompany { _id name } toStatusNode { _id label }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind publicationStart publicationEnd status closed vacancyNo applyUrl vacancyUrl campaign workLocation creationDate modificationDate toVacancy { _id jobTitle } toMedium { _id } toCompany { _id name } toStatusNode { _id label }","connection":"<connection id>"}
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind publicationStart publicationEnd status closed vacancyNo applyUrl vacancyUrl campaign workLocation creationDate modificationDate toVacancy { _id jobTitle } toMedium { _id } toCompany { _id name } toStatusNode { _id label }","cursor":"","connection":"<connection id>"}
```

## `carerix.listUsers@2` — Carerix — List users

Lists the recruiters and consultants inside your Carerix environment.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 users rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Users per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind firstName lastName lastNamePrefix emailAddress phoneNumberBusiness mobileNumber jobTitle isActive creationDate modificationDate toCompany { _id name }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `users` array — Users · `userCount` number — Users in this answer · `hasMore` boolean — More pages are waiting behind this one · `totalElements` number — Records Carerix says match · `page` number — The page this answer came from · `nextPage` number — The next page number — absent on the last page · `nextCursor` string — Cursor for the next page.

```json
{"type":"carerix.listUsers","version":2,"label":"List users","config":{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind firstName lastName lastNamePrefix emailAddress phoneNumberBusiness mobileNumber jobTitle isActive creationDate modificationDate toCompany { _id name }","cursor":"","connection":"<connection id>"}}
```

### Older version `carerix.listUsers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 users rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Users per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind firstName lastName lastNamePrefix emailAddress phoneNumberBusiness mobileNumber jobTitle isActive creationDate modificationDate toCompany { _id name }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind firstName lastName lastNamePrefix emailAddress phoneNumberBusiness mobileNumber jobTitle isActive creationDate modificationDate toCompany { _id name }","connection":"<connection id>"}
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind firstName lastName lastNamePrefix emailAddress phoneNumberBusiness mobileNumber jobTitle isActive creationDate modificationDate toCompany { _id name }","cursor":"","connection":"<connection id>"}
```

## `carerix.listVacancies@2` — Carerix — List vacancies

Finds the vacancies in Carerix, with their client company and owner.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 vacancies rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Vacancies per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind vacancyNo jobTitle companyName startDate endDate deadline numberOfVacancies isConfidential creationDate modificationDate toCompany { _id name } owner { _id firstName lastName } toStatusNode { _id label }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `vacancies` array — Vacancies · `vacancyCount` number — Vacancies in this answer · `hasMore` boolean — More pages are waiting behind this one · `totalElements` number — Records Carerix says match · `page` number — The page this answer came from · `nextPage` number — The next page number — absent on the last page · `nextCursor` string — Cursor for the next page.

```json
{"type":"carerix.listVacancies","version":2,"label":"List vacancies","config":{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind vacancyNo jobTitle companyName startDate endDate deadline numberOfVacancies isConfidential creationDate modificationDate toCompany { _id name } owner { _id firstName lastName } toStatusNode { _id label }","cursor":"","connection":"<connection id>"}}
```

### Older version `carerix.listVacancies@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `qualifier` | text, default `""` | Qualifier: A Carerix qualifier, the same expression the Carerix client's own searches use — for example toStatusNode.code='active' or modificationDate>'2026-09-01 00:00:00'. Leave it empty for every record. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time. |
| `returnAll` | true/false, default `false` | Fetch every page: Climbs Carerix's page numbers until the last one, and refuses past 5,000 vacancies rather than answering a short list. |
| `page` | number, default `0` | Page: Carerix counts pages from 0. |
| `size` | number, default `100` | Vacancies per page: 1 to 100 — Carerix answers at most 100 records per request. |
| `includeDeleted` | true/false, default `false` | Include deleted records: Carerix keeps deleted records; off, they stay out of the answer. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |
| `fields` | text, default `"_id _kind vacancyNo jobTitle companyName startDate endDate deadline numberOfVacancies isConfidential creationDate modificationDate toCompany { _id name } owner { _id firstName lastName } toStatusNode { _id label }"` | Fields to fetch: The selection set Carerix answers, written as GraphQL fields separated by spaces. Add a field here to get it back; docs.carerix.io/graphql lists them all. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind vacancyNo jobTitle companyName startDate endDate deadline numberOfVacancies isConfidential creationDate modificationDate toCompany { _id name } owner { _id firstName lastName } toStatusNode { _id label }","connection":"<connection id>"}
{"qualifier":"","returnAll":false,"page":0,"size":100,"includeDeleted":false,"language":"","fields":"_id _kind vacancyNo jobTitle companyName startDate endDate deadline numberOfVacancies isConfidential creationDate modificationDate toCompany { _id name } owner { _id firstName lastName } toStatusNode { _id label }","cursor":"","connection":"<connection id>"}
```

## `carerix.query@1` — Carerix — Run GraphQL query

Runs any Carerix GraphQL query or mutation through a saved connection.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `query` | text, required | Query: A GraphQL query or mutation. Carerix's whole schema is at docs.carerix.io/graphql. Values from earlier nodes belong in Variables, not pasted into this text. — plain text, never `{{ }}` |
| `variables` | object (any keys), default `{}` | Variables: The variables object sent beside the query. Every value an earlier node produces belongs here. |
| `operationName` | text, default `""` | Operation name: Only needed when the document holds more than one operation. |
| `documentKind` | "read" or "write", default `"read"` | What this document does: A read may be re-run freely and is never confirmed. Choose the other one for any document holding a mutation: a mutation run twice can create two records. |

Output: `data` object — What Carerix answered.

```json
{"type":"carerix.query","version":1,"label":"Run GraphQL query","config":{"variables":{},"operationName":"","documentKind":"read","connection":"<connection id>","query":"<query>"}}
```

## `carerix.sendTemplateEmail@1` — Carerix — Send template e-mail

Sends a Carerix e-mail template about one of its records.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `about` | "candidate" or "match" or "vacancy" or "company" or "publication" or "lead" or "opportunity" or "talentPool" or "user" or "job" or "invoice" or "activity", default `"candidate"` | About: Which Carerix record the e-mail is about — it decides where Carerix logs it. |
| `recordId` | text, default `""` | Record id: The _id of that record. |
| `emailTemplate` | text, default `""` | Template code: The code of the Carerix e-mail template — not its title. Find it under the template in Carerix. |
| `fromAddress` | text, default `""` | From: Empty lets Carerix use the environment's own sender. |
| `toAddress` | text, default `""` | To: Empty lets Carerix address the record's own e-mail address. |

Output: `activity` object — The activity Carerix logged.

```json
{"type":"carerix.sendTemplateEmail","version":1,"label":"Send template e-mail","config":{"about":"candidate","recordId":"","emailTemplate":"","fromAddress":"","toAddress":"","connection":"<connection id>"}}
```

## `carerix.updateCandidate@1` — Carerix — Update candidate

Changes fields on a Carerix candidate that already exists.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `candidateId` | text, default `""` | Candidate id: The _id of the candidate to change. Fields left empty are left alone. Carerix does not support patch semantics: a list field you send — attachments, skills, educations, phone numbers — REPLACES that whole list with what you send. Leave a list field out to leave it alone. |
| `firstName` | text, default `""` | First name |
| `lastName` | text, default `""` | Last name |
| `lastNamePrefix` | text, default `""` | Last-name prefix |
| `emailAddress` | text, default `""` | E-mail address |
| `mobileNumber` | text, default `""` | Mobile number |
| `phoneNumber` | text, default `""` | Phone number |
| `street` | text, default `""` | Street |
| `houseNumber` | text, default `""` | House number |
| `postalCode` | text, default `""` | Postal code |
| `city` | text, default `""` | City |
| `birthDate` | text, default `""` | Date of birth: Carerix dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, never ISO-8601. |
| `availableDate` | text, default `""` | Available from |
| `notes` | text, default `""` | Notes |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CREmployeeRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |

Output: `candidate` object — Candidate.

```json
{"type":"carerix.updateCandidate","version":1,"label":"Update candidate","config":{"candidateId":"","firstName":"","lastName":"","lastNamePrefix":"","emailAddress":"","mobileNumber":"","phoneNumber":"","street":"","houseNumber":"","postalCode":"","city":"","birthDate":"","availableDate":"","notes":"","extraFields":{},"language":"","connection":"<connection id>"}}
```

## `carerix.updateCompany@1` — Carerix — Update company

Changes a Carerix client company that already exists.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `companyId` | text, default `""` | Company id: The _id of the company to change. Fields left empty are left alone. Carerix does not support patch semantics: a list field you send — attachments, skills, educations, phone numbers — REPLACES that whole list with what you send. Leave a list field out to leave it alone. |
| `name` | text, default `""` | Company name |
| `shortName` | text, default `""` | Short name |
| `visitStreet` | text, default `""` | Street |
| `visitNumber` | text, default `""` | House number |
| `visitPostalCode` | text, default `""` | Postal code |
| `visitCity` | text, default `""` | City |
| `phoneNumber` | text, default `""` | Phone number |
| `emailAddress` | text, default `""` | E-mail address |
| `url` | text, default `""` | Website |
| `debtorNumber` | text, default `""` | Debtor number |
| `ownerUserId` | text, default `""` | Owner id |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CRCompanyRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |

Output: `company` object — Company.

```json
{"type":"carerix.updateCompany","version":1,"label":"Update company","config":{"companyId":"","name":"","shortName":"","visitStreet":"","visitNumber":"","visitPostalCode":"","visitCity":"","phoneNumber":"","emailAddress":"","url":"","debtorNumber":"","ownerUserId":"","extraFields":{},"connection":"<connection id>"}}
```

## `carerix.updateMatch@1` — Carerix — Update match

Changes a Carerix match — its notes, rates, start date or stage.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `matchId` | text, default `""` | Match id: The _id of the match to change. Fields left empty, and rates left at zero, are left alone. Carerix does not support patch semantics: a list field you send — attachments, skills, educations, phone numbers — REPLACES that whole list with what you send. Leave a list field out to leave it alone. |
| `statusInfoId` | text, default `""` | Pipeline stage id: The CRStatusInfo record to attach — a match's stage is a record of its own. Run GraphQL query over crStatusInfoPage gives you the id. |
| `motivation` | text, default `""` | Motivation |
| `notes` | text, default `""` | Notes |
| `jobStartDate` | text, default `""` | Job starts: Carerix dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, never ISO-8601. |
| `wageRate` | number, default `0` | Wage rate: Zero means 'leave it as it is' — Carerix is never sent a rate of 0 from here. |
| `purchaseRate` | number, default `0` | Purchase rate |
| `invoiceRate` | number, default `0` | Invoice rate |
| `sellingPrice` | number, default `0` | Selling price |
| `costPrice` | number, default `0` | Cost price |
| `salesFactor` | number, default `0` | Sales factor |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CRMatchRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |

Output: `match` object — Match.

```json
{"type":"carerix.updateMatch","version":1,"label":"Update match","config":{"matchId":"","statusInfoId":"","motivation":"","notes":"","jobStartDate":"","wageRate":0,"purchaseRate":0,"invoiceRate":0,"sellingPrice":0,"costPrice":0,"salesFactor":0,"extraFields":{},"connection":"<connection id>"}}
```

## `carerix.updateVacancy@1` — Carerix — Update vacancy

Changes a Carerix vacancy that already exists.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `vacancyId` | text, default `""` | Vacancy id: The _id of the vacancy to change. Fields left empty are left alone. Carerix does not support patch semantics: a list field you send — attachments, skills, educations, phone numbers — REPLACES that whole list with what you send. Leave a list field out to leave it alone. |
| `jobTitle` | text, default `""` | Job title |
| `companyId` | text, default `""` | Company id |
| `ownerUserId` | text, default `""` | Owner id |
| `startDate` | text, default `""` | Starts: Carerix dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, never ISO-8601. |
| `endDate` | text, default `""` | Ends |
| `deadline` | text, default `""` | Deadline |
| `workCity` | text, default `""` | Work city |
| `requirements` | text, default `""` | Requirements |
| `offerInformation` | text, default `""` | What you offer |
| `vacancyInformation` | text, default `""` | The vacancy text |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CRVacancyRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |

Output: `vacancy` object — Vacancy.

```json
{"type":"carerix.updateVacancy","version":1,"label":"Update vacancy","config":{"vacancyId":"","jobTitle":"","companyId":"","ownerUserId":"","startDate":"","endDate":"","deadline":"","workCity":"","requirements":"","offerInformation":"","vacancyInformation":"","extraFields":{},"connection":"<connection id>"}}
```

## `carerix.uploadAttachment@1` — Carerix — Upload attachment

Adds a document or CV to Carerix.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `carerix`) |
| `fileName` | text, required | File name: The name Carerix stores and offers the document under, extension included. |
| `contentBase64` | text, required | Contents (base64): The file itself, as base64 — {{ file("cv.pdf").base64 }} from the file store, or the output of the Download attachment node. Carerix accepts 10 MB per request and base64 is a third wider than the bytes, so anything over 7 MB is refused here. |
| `label` | text, default `""` | Label: What Carerix shows the document as. Empty uses the file name. |
| `ownerUserId` | text, default `""` | Owner id |
| `typeNodeValue` | text, default `""` | Document type code: The code of the Carerix document type, looked up by code rather than by id — for example cv or contract. |
| `extraFields` | object (any keys), default `{}` | Other fields: Any other CRAttachmentRequest field, by name — docs.carerix.io/graphql has the full list. Dates are yyyy-MM-dd HH:mm:ss in Amsterdam time, and a list value is set by its code with a _lookup. |
| `language` | "" or "DUTCH" or "ENGLISH" or "FRENCH" or "GERMAN" or "HUNGARIAN" or "ITALIAN" or "POLISH" or "PORTUGUESE" or "SPANISH", default `""` | List-value language: Which language Carerix answers its list values (statuses, types, results) in. It does not translate the data. |

Output: `attachmentId` string — New attachment id · `attachment` object — Attachment.

```json
{"type":"carerix.uploadAttachment","version":1,"label":"Upload attachment","config":{"label":"","ownerUserId":"","typeNodeValue":"","extraFields":{},"language":"","connection":"<connection id>","fileName":"<file name>","contentBase64":"<contents (base64)>"}}
```


---

## File: references/nodes/claude-nodes.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Claude nodes (`claude.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `claude.classify@1` — Claude — Classify

Sorts a text into one of your labels with Claude and sends the flow down that path.

Ports: one per row `id` in `labels`, plus `other` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Your own Anthropic API key. Every call runs on it and is billed by Anthropic to your account. — a connection id from `requires.connections` (provider `anthropic`) |
| `model` | text, required | Model: The models your Anthropic connection listed when its key was saved. Refresh the connection to see a model Anthropic added since. Automatic retries only help when the model never started answering. — an AI model id |
| `text` | text, required | Text: What is being sorted. Everything here is sent to the model. |
| `labels` | list of {id: text, label: text, description?: text}, default `[]` | Labels: Each label becomes an output on the canvas. Because this step decides where the flow goes next, it cannot be turned off and it cannot continue on error — delete it instead. |
| `whenNoMatch` | "other" or "fail", default `"other"` | When nothing fits: Every text ends somewhere: down the Other path, or with the step failing. A label the model invents counts as nothing fitting. |
| `instructions` | text, default `""` | Instructions: Anything the model should know: what a borderline text should count as, or which label wins when two could fit. |
| `reasoning` | "default" or "low" or "medium" or "high", default `"default"` | Extended thinking: How hard Claude may think before it answers. Model default leaves it to the model; a level turns extended thinking up — adaptive thinking on the newest Claude models, a thinking budget on older ones. Thinking is billed by Anthropic to your account as output tokens, on top of the answer, and makes the step slower. |

Output: shaped by the config.

```json
{"type":"claude.classify","version":1,"label":"Classify","config":{"labels":[],"whenNoMatch":"other","instructions":"","reasoning":"default","connection":"<connection id>","model":"<model>","text":"<text>"}}
```

## `claude.extract@1` — Claude — Extract fields

Pulls the values you name out of a text or a document with Claude, each typed.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Your own Anthropic API key. Every call runs on it and is billed by Anthropic to your account. — a connection id from `requires.connections` (provider `anthropic`) |
| `model` | text, required | Model: The models your Anthropic connection listed when its key was saved. Refresh the connection to see a model Anthropic added since. Automatic retries only help when the model never started answering. — an AI model id |
| `source` | "text" or "file", default `"text"` | Read from: Text you point at with an expression, or a file in this workspace. |
| `text` | text, default `""` | Text: Everything here is sent to the model — point at the part that holds the values. — used when `source` is "text" |
| `file` | text, default `""` | File: Claude reads a PDF or a JPEG, PNG, GIF or WebP image itself, tables and pictures included. Word, Excel, OpenDocument, HTML, XML and CSV files are read as text first, and plain text as it is. Up to 5 MB. — a file name, listed in `requires.files`; used when `source` is "file" |
| `fields` | list of {name: text, description?: text, type?: "text" or "number" or "boolean" or "date" or "text-list" or "number-list", required?: true/false}, optional | Fields: Add the fields you want back. A sentence saying where to find each one makes this much more accurate — “the total including VAT, near the bottom”. |
| `instructions` | text, default `""` | Instructions: Anything the model should know about the source before it reads it. |
| `whenMissing` | "empty" or "fail", default `"empty"` | When a field is missing: A value the source does not carry comes back empty. Failing instead stops the run on any field you marked required, and says which one. |
| `useJsonSchema` | true/false, default `false` | Use JSON Schema instead of fields: For an answer the field rows cannot describe — nesting, enums, a list of objects. It replaces the fields above entirely. |
| `schema` | object (any keys), default `{}` | Schema: A JSON Schema document. The answer is validated against it and one repair round is allowed before the step fails. — used when `useJsonSchema` is true |
| `reasoning` | "default" or "low" or "medium" or "high", default `"default"` | Extended thinking: How hard Claude may think before it answers. Model default leaves it to the model; a level turns extended thinking up — adaptive thinking on the newest Claude models, a thinking budget on older ones. Thinking is billed by Anthropic to your account as output tokens, on top of the answer, and makes the step slower. |

Output: shaped by the config.

```json
{"type":"claude.extract","version":1,"label":"Extract fields","config":{"source":"text","text":"","file":"","instructions":"","whenMissing":"empty","useJsonSchema":false,"schema":{},"reasoning":"default","connection":"<connection id>","model":"<model>","fields":[]}}
```

## `claude.generate@1` — Claude — Generate text

Asks Claude to write something from your instructions and the flow's data.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Your own Anthropic API key. Every call runs on it and is billed by Anthropic to your account. — a connection id from `requires.connections` (provider `anthropic`) |
| `model` | text, required | Model: The models your Anthropic connection listed when its key was saved. Refresh the connection to see a model Anthropic added since. Automatic retries only help when the model never started answering. — an AI model id |
| `task` | text, default `"Custom"` | What to do: Picks the standing instruction the model gets before your prompt. Complete a suggestion in your own words — “Translate to Dutch” — or choose Custom and say everything in the prompt. |
| `prompt` | text, required | Prompt: What you want, and the data it is about. Everything here is sent to the model, so point at the values you need and not at the whole payload. |
| `output` | "text" or "fields", default `"text"` | Output: Text answers one piece of writing. Fields turns the answer into a typed object, the way Extract fields does. |
| `fields` | list of {name: text, description?: text, type?: "text" or "number" or "boolean" or "date" or "text-list" or "number-list", required?: true/false}, default `[]` | Fields: What you want back, and a sentence saying where to find each one — that sentence is most of what makes the answer right. — used when `output` is "fields" |
| `creativity` | number, optional | Creativity: 0 to 1. Low keeps the answer close to what you asked for; high lets the model wander, which is rarely what a flow wants. Claude Opus 4.7 and later and Claude Sonnet 5 set their own and ignore it, and so does any Claude model while it thinks. |
| `maxLength` | number, default `1024` | Maximum length: In tokens — roughly three quarters of a word each. The model stops here, so a ceiling that is too low cuts the answer off mid-sentence. |
| `reasoning` | "default" or "low" or "medium" or "high", default `"default"` | Extended thinking: How hard Claude may think before it answers. Model default leaves it to the model; a level turns extended thinking up — adaptive thinking on the newest Claude models, a thinking budget on older ones. Thinking is billed by Anthropic to your account as output tokens, on top of the answer, and makes the step slower. |

Output: `text` string — Text · more fields follow from the config.

```json
{"type":"claude.generate","version":1,"label":"Generate text","config":{"task":"Custom","output":"text","fields":[],"maxLength":1024,"reasoning":"default","connection":"<connection id>","model":"<model>","prompt":"<prompt>"}}
```


---

## File: references/nodes/clockify.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Clockify nodes (`clockify.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `clockify.createClient@1` — Clockify — Create client

Adds a client to the Clockify workspace.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `name` | text, required | Client name: Up to 100 characters. |
| `email` | text, default `""` | E-mail |
| `address` | text, default `""` | Address: Up to 3,000 characters. |
| `note` | text, default `""` | Note: Up to 3,000 characters. |

Output: `clientId` string — Client id · `client` object — Client.

```json
{"type":"clockify.createClient","version":1,"label":"Create client","config":{"workspaceId":"","email":"","address":"","note":"","connection":"<connection id>","name":"<client name>"}}
```

## `clockify.createProject@1` — Clockify — Create project

Creates a Clockify project under a client, with rates and an estimate.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `name` | text, required | Project name: Up to 250 characters. Clockify allows two projects with the same name. |
| `clientId` | text, default `""` | Client id: The List clients node gives you the id. |
| `color` | text, default `""` | Colour: The colour Clockify draws the project in, as six hex digits after a hash. Leave empty to let Clockify choose. |
| `billable` | true/false, default `true` | Billable |
| `isPublic` | true/false, default `true` | Everyone in the workspace can see it |
| `note` | text, default `""` | Note: Up to 16,384 characters. |
| `estimateType` | "" or "AUTO" or "MANUAL", default `""` | Estimate |
| `estimate` | text, default `""` | Estimated time: An ISO 8601 duration — PT40H is forty hours, PT1H30M is an hour and a half. |
| `hourlyRateAmount` | number, default `0` | Hourly rate: Clockify stores a rate as a whole number in the workspace currency's smallest unit, so 75.00 is 7500. Zero leaves the rate unset. |
| `hourlyRateSince` | text, default `""` | Hourly rate from |
| `costRateAmount` | number, default `0` | Cost rate: Also a whole number in the currency's smallest unit. Zero leaves it unset. |
| `costRateSince` | text, default `""` | Cost rate from |
| `userIds` | text, default `""` | Members: The people to put on the project — pick from this node's workspace, or type Clockify user ids. — a value from the vendor's own list |

Output: `projectId` string — Project id · `project` object — Project.

```json
{"type":"clockify.createProject","version":1,"label":"Create project","config":{"workspaceId":"","clientId":"","color":"","billable":true,"isPublic":true,"note":"","estimateType":"","estimate":"","hourlyRateAmount":0,"hourlyRateSince":"","costRateAmount":0,"costRateSince":"","userIds":"","connection":"<connection id>","name":"<project name>"}}
```

## `clockify.createTag@1` — Clockify — Create tag

Adds a tag to the Clockify workspace.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `name` | text, required | Tag name: Up to 100 characters. |

Output: `tagId` string — Tag id · `tag` object — Tag.

```json
{"type":"clockify.createTag","version":1,"label":"Create tag","config":{"workspaceId":"","connection":"<connection id>","name":"<tag name>"}}
```

## `clockify.createTask@1` — Clockify — Create task

Adds a task to a Clockify project.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `projectId` | text, required | Project id: The project the task belongs to. |
| `name` | text, required | Task name: Up to 1,000 characters. |
| `assigneeIds` | text, default `""` | Assignees: Pick from this node's workspace, or type Clockify user ids. — a value from the vendor's own list |
| `userGroupIds` | text, default `""` | User groups — a value from the vendor's own list |
| `estimate` | text, default `""` | Estimated time: An ISO 8601 duration — PT8H is eight hours. |
| `budgetEstimate` | number, default `0` | Budget: A whole number in the workspace currency's smallest unit, so 500.00 is 50000. Zero leaves it unset. |
| `status` | "ACTIVE" or "DONE", default `"ACTIVE"` | Status |

Output: `taskId` string — Task id · `task` object — Task.

```json
{"type":"clockify.createTask","version":1,"label":"Create task","config":{"workspaceId":"","assigneeIds":"","userGroupIds":"","estimate":"","budgetEstimate":0,"status":"ACTIVE","connection":"<connection id>","projectId":"<project id>","name":"<task name>"}}
```

## `clockify.createTimeEntry@1` — Clockify — Create time entry

Logs hours in Clockify, for yourself or for someone else.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `userId` | text, default `""` | User id: Empty logs the hours for the person the API key belongs to. Filling it in logs them for a colleague, which the key's user must be allowed to do. |
| `start` | text, required | Start: When the work started, in UTC. |
| `end` | text, default `""` | End: Leave empty to start a running timer — use Stop timer to end it. |
| `description` | text, default `""` | Description: Up to 3,000 characters. |
| `projectId` | text, default `""` | Project id |
| `taskId` | text, default `""` | Task id |
| `tagIds` | text, default `""` | Tag ids — a value from the vendor's own list |
| `billable` | true/false, default `false` | Billable |
| `type` | "REGULAR" or "BREAK", default `"REGULAR"` | Kind |
| `customFields` | object of text, default `{}` | Custom fields: Clockify keys custom fields by their id — the List custom fields node gives you the ids. A field the workspace marks required must be filled in here or Clockify refuses the entry. |

Output: `timeEntryId` string — Time entry id · `timeEntry` object — Time entry · `isRunning` boolean — The entry is a running timer.

```json
{"type":"clockify.createTimeEntry","version":1,"label":"Create time entry","config":{"workspaceId":"","userId":"","end":"","description":"","projectId":"","taskId":"","tagIds":"","billable":false,"type":"REGULAR","customFields":{},"connection":"<connection id>","start":"<start>"}}
```

## `clockify.createTimeOffRequest@1` — Clockify — Request time off

Submits a Clockify time off request for yourself or a colleague.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `policyId` | text, required | Policy id: Which kind of leave this is — the List time off policies node gives you the ids. |
| `userId` | text, default `""` | User id: Empty files the request for the person the API key belongs to. Filing it for a colleague needs the right role in Clockify. |
| `startDate` | text, required | First day |
| `endDate` | text, required | Last day |
| `days` | number, default `0` | Days: How many days to take off the balance. Zero lets Clockify work it out from the dates. |
| `isHalfDay` | true/false, default `false` | Half a day |
| `halfDayPeriod` | "FIRST_HALF" or "SECOND_HALF" or "NOT_DEFINED", default `"NOT_DEFINED"` | Which half — used when `isHalfDay` is true |
| `note` | text, default `""` | Note |

Output: `requestId` string — Request id · `request` object — Time off request.

```json
{"type":"clockify.createTimeOffRequest","version":1,"label":"Request time off","config":{"workspaceId":"","userId":"","days":0,"isHalfDay":false,"halfDayPeriod":"NOT_DEFINED","note":"","connection":"<connection id>","policyId":"<policy id>","startDate":"<first day>","endDate":"<last day>"}}
```

## `clockify.deleteTimeEntry@1` — Clockify — Delete time entry

Removes a time entry from Clockify.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `timeEntryId` | text, required | Time entry id: The entry to remove — the List time entries node gives you the id. |

Output: `timeEntryId` string — Time entry id · `deleted` boolean — This run removed it.

```json
{"type":"clockify.deleteTimeEntry","version":1,"label":"Delete time entry","config":{"workspaceId":"","connection":"<connection id>","timeEntryId":"<time entry id>"}}
```

## `clockify.detailedReport@2` — Clockify — Detailed report

Runs Clockify's detailed report and returns its rows or a file.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `dateRangeType` | "ABSOLUTE" or "TODAY" or "YESTERDAY" or "THIS_WEEK" or "LAST_WEEK" or "PAST_TWO_WEEKS" or "THIS_MONTH" or "LAST_MONTH" or "THIS_YEAR" or "LAST_YEAR", default `"LAST_MONTH"` | Period |
| `dateRangeStart` | text, default `""` | From — used when `dateRangeType` is "ABSOLUTE" |
| `dateRangeEnd` | text, default `""` | To — used when `dateRangeType` is "ABSOLUTE" |
| `exportType` | "JSON" or "CSV" or "XLSX" or "PDF" or "ZIP", default `"JSON"` | Answer as: Rows come back as data you can loop over. Every other choice comes back as a file — which shape Clockify sends that file in is owed one run against a real workspace. |
| `userIds` | text, default `""` | People: Pick from this node's workspace, or type Clockify user ids. — a value from the vendor's own list |
| `projectIds` | text, default `""` | Projects: Pick from this node's workspace, or type Clockify project ids. — a value from the vendor's own list |
| `clientIds` | text, default `""` | Clients: Pick from this node's workspace, or type Clockify client ids. — a value from the vendor's own list |
| `taskIds` | text, default `""` | Tasks — a value from the vendor's own list |
| `tagIds` | text, default `""` | Tags — a value from the vendor's own list |
| `billable` | "" or "true" or "false", default `""` | Billable |
| `approvalState` | "ALL" or "APPROVED" or "UNAPPROVED", default `"ALL"` | Approval |
| `invoicingState` | "ALL" or "INVOICED" or "UNINVOICED", default `"ALL"` | Invoicing |
| `description` | text, default `""` | Description contains |
| `withoutDescription` | true/false, default `false` | Only entries with no description |
| `rounding` | true/false, default `false` | Round the way the workspace does |
| `amountShown` | "HIDE_AMOUNT" or "EARNED" or "COST" or "PROFIT" or "EXPORT", default `"HIDE_AMOUNT"` | Amounts |
| `sortColumn` | "DATE" or "ID" or "DESCRIPTION" or "USER" or "DURATION" or "ZONED_DATE" or "NATURAL" or "USER_DATE", default `"DATE"` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `timeZone` | text, default `""` | Time zone: Leave empty to use the key's own Clockify time zone. |
| `weekStart` | "MONDAY" or "TUESDAY" or "WEDNESDAY" or "THURSDAY" or "FRIDAY" or "SATURDAY" or "SUNDAY", default `"MONDAY"` | Week starts on |
| `pageSize` | number, default `200` | Rows per call: 1 to 1,000. Clockify publishes no maximum here, so this is Flowmage's cap. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's report pages, and refuses past 5,000 rows rather than answering a short list. A file export is one call by definition. — used when `exportType` is "JSON" |
| `filter` | object (any keys), default `{}` | Anything else: Anything else from Clockify's report filter, merged over the fields above — for example {"customFields":[{"id":"…","value":"…"}]}. The fields above win only where this leaves a key out. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `timeEntries` array — Report rows · `totals` array — Totals · `timeEntryCount` number — Rows in this answer · `hasMore` boolean — More rows are waiting behind this page · `file` any — Report file, or nothing when the answer is rows · `file.base64` string — Report file (base64) · `file.name` string — File name · `file.contentType` string — Content type · `file.size` number — Size in bytes · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.detailedReport","version":2,"label":"Detailed report","config":{"workspaceId":"","dateRangeType":"LAST_MONTH","dateRangeStart":"","dateRangeEnd":"","exportType":"JSON","userIds":"","projectIds":"","clientIds":"","taskIds":"","tagIds":"","billable":"","approvalState":"ALL","invoicingState":"ALL","description":"","withoutDescription":false,"rounding":false,"amountShown":"HIDE_AMOUNT","sortColumn":"DATE","sortOrder":"ASCENDING","timeZone":"","weekStart":"MONDAY","pageSize":200,"returnAll":false,"filter":{},"cursor":"","connection":"<connection id>"}}
```

### Older version `clockify.detailedReport@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `dateRangeType` | "ABSOLUTE" or "TODAY" or "YESTERDAY" or "THIS_WEEK" or "LAST_WEEK" or "PAST_TWO_WEEKS" or "THIS_MONTH" or "LAST_MONTH" or "THIS_YEAR" or "LAST_YEAR", default `"LAST_MONTH"` | Period |
| `dateRangeStart` | text, default `""` | From — used when `dateRangeType` is "ABSOLUTE" |
| `dateRangeEnd` | text, default `""` | To — used when `dateRangeType` is "ABSOLUTE" |
| `exportType` | "JSON" or "CSV" or "XLSX" or "PDF" or "ZIP", default `"JSON"` | Answer as: Rows come back as data you can loop over. Every other choice comes back as a file — which shape Clockify sends that file in is owed one run against a real workspace. |
| `userIds` | text, default `""` | People: Pick from this node's workspace, or type Clockify user ids. — a value from the vendor's own list |
| `projectIds` | text, default `""` | Projects: Pick from this node's workspace, or type Clockify project ids. — a value from the vendor's own list |
| `clientIds` | text, default `""` | Clients: Pick from this node's workspace, or type Clockify client ids. — a value from the vendor's own list |
| `taskIds` | text, default `""` | Tasks — a value from the vendor's own list |
| `tagIds` | text, default `""` | Tags — a value from the vendor's own list |
| `billable` | "" or "true" or "false", default `""` | Billable |
| `approvalState` | "ALL" or "APPROVED" or "UNAPPROVED", default `"ALL"` | Approval |
| `invoicingState` | "ALL" or "INVOICED" or "UNINVOICED", default `"ALL"` | Invoicing |
| `description` | text, default `""` | Description contains |
| `withoutDescription` | true/false, default `false` | Only entries with no description |
| `rounding` | true/false, default `false` | Round the way the workspace does |
| `amountShown` | "HIDE_AMOUNT" or "EARNED" or "COST" or "PROFIT" or "EXPORT", default `"HIDE_AMOUNT"` | Amounts |
| `sortColumn` | "DATE" or "ID" or "DESCRIPTION" or "USER" or "DURATION" or "ZONED_DATE" or "NATURAL" or "USER_DATE", default `"DATE"` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `timeZone` | text, default `""` | Time zone: Leave empty to use the key's own Clockify time zone. |
| `weekStart` | "MONDAY" or "TUESDAY" or "WEDNESDAY" or "THURSDAY" or "FRIDAY" or "SATURDAY" or "SUNDAY", default `"MONDAY"` | Week starts on |
| `pageSize` | number, default `200` | Rows per call: 1 to 1,000. Clockify publishes no maximum here, so this is Flowmage's cap. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's report pages, and refuses past 5,000 rows rather than answering a short list. A file export is one call by definition. — used when `exportType` is "JSON" |
| `filter` | object (any keys), default `{}` | Anything else: Anything else from Clockify's report filter, merged over the fields above — for example {"customFields":[{"id":"…","value":"…"}]}. The fields above win only where this leaves a key out. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","dateRangeType":"LAST_MONTH","dateRangeStart":"","dateRangeEnd":"","exportType":"JSON","userIds":"","projectIds":"","clientIds":"","taskIds":"","tagIds":"","billable":"","approvalState":"ALL","invoicingState":"ALL","description":"","withoutDescription":false,"rounding":false,"amountShown":"HIDE_AMOUNT","sortColumn":"DATE","sortOrder":"ASCENDING","timeZone":"","weekStart":"MONDAY","pageSize":200,"returnAll":false,"filter":{},"connection":"<connection id>"}
{"workspaceId":"","dateRangeType":"LAST_MONTH","dateRangeStart":"","dateRangeEnd":"","exportType":"JSON","userIds":"","projectIds":"","clientIds":"","taskIds":"","tagIds":"","billable":"","approvalState":"ALL","invoicingState":"ALL","description":"","withoutDescription":false,"rounding":false,"amountShown":"HIDE_AMOUNT","sortColumn":"DATE","sortOrder":"ASCENDING","timeZone":"","weekStart":"MONDAY","pageSize":200,"returnAll":false,"filter":{},"cursor":"","connection":"<connection id>"}
```

## `clockify.getTimeOffBalance@2` — Clockify — Get time off balance

Shows a person's time off balances per Clockify policy.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `userId` | text, default `""` | User id: Empty reads the balances of the person the API key belongs to — the List users node gives you an id. |
| `sort` | "" or "USER" or "POLICY" or "USED" or "BALANCE" or "TOTAL", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Rows per call: 1 to 200 — that is Clockify's own maximum here. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `balances` array — Balances · `balanceCount` number — Balances in this answer · `hasMore` boolean — More balances are waiting behind this page · `nextPage` number — The page to ask for next, or 0 · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.getTimeOffBalance","version":2,"label":"Get time off balance","config":{"workspaceId":"","userId":"","sort":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `clockify.getTimeOffBalance@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `userId` | text, default `""` | User id: Empty reads the balances of the person the API key belongs to — the List users node gives you an id. |
| `sort` | "" or "USER" or "POLICY" or "USED" or "BALANCE" or "TOTAL", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Rows per call: 1 to 200 — that is Clockify's own maximum here. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","userId":"","sort":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"connection":"<connection id>"}
{"workspaceId":"","userId":"","sort":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `clockify.listClients@2` — Clockify — List clients

Lists the clients in the Clockify workspace.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `name` | text, default `""` | Name contains |
| `archived` | "" or "true" or "false", default `""` | Archived |
| `sortColumn` | "NAME" or "ID", default `"NAME"` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Clients per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 clients rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `clients` array — Clients · `clientCount` number — Clients in this answer · `hasMore` boolean — More clients are waiting behind this page · `nextPage` number — The page to ask for next, or 0 · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.listClients","version":2,"label":"List clients","config":{"workspaceId":"","name":"","archived":"","sortColumn":"NAME","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `clockify.listClients@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `name` | text, default `""` | Name contains |
| `archived` | "" or "true" or "false", default `""` | Archived |
| `sortColumn` | "NAME" or "ID", default `"NAME"` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Clients per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 clients rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","name":"","archived":"","sortColumn":"NAME","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"connection":"<connection id>"}
{"workspaceId":"","name":"","archived":"","sortColumn":"NAME","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `clockify.listCustomFields@1` — Clockify — List custom fields

Lists the Clockify custom fields and their ids.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `name` | text, default `""` | Name contains |
| `status` | "" or "INACTIVE" or "VISIBLE" or "INVISIBLE", default `""` | Status |
| `entityType` | text, default `""` | Belongs to: Which kind of record the field hangs off. Leave empty for all of them; Clockify publishes no closed list, so anything it accepts is accepted here. |

Output: `customFields` array — Custom fields · `customFieldCount` number — Custom fields in this answer.

```json
{"type":"clockify.listCustomFields","version":1,"label":"List custom fields","config":{"workspaceId":"","name":"","status":"","entityType":"","connection":"<connection id>"}}
```

## `clockify.listProjects@2` — Clockify — List projects

Lists Clockify projects, with their clients and budgets.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `name` | text, default `""` | Name contains: Part of the project's name. |
| `strictNameSearch` | true/false, default `false` | Match the name exactly |
| `archived` | "" or "true" or "false", default `""` | Archived |
| `billable` | "" or "true" or "false", default `""` | Billable |
| `clientIds` | text, default `""` | Client ids: Pick from this node's workspace, or type Clockify client ids. — a value from the vendor's own list |
| `userIds` | text, default `""` | User ids: Only projects these people are on. — a value from the vendor's own list |
| `access` | "" or "PUBLIC" or "PRIVATE", default `""` | Access |
| `hydrated` | true/false, default `false` | Include client, task and membership details |
| `sortColumn` | "" or "ID" or "NAME" or "CLIENT_NAME" or "DURATION" or "BUDGET" or "PROGRESS", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Projects per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 projects rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `projects` array — Projects · `projectCount` number — Projects in this answer · `hasMore` boolean — More projects are waiting behind this page · `nextPage` number — The page to ask for next, or 0 · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.listProjects","version":2,"label":"List projects","config":{"workspaceId":"","name":"","strictNameSearch":false,"archived":"","billable":"","clientIds":"","userIds":"","access":"","hydrated":false,"sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `clockify.listProjects@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `name` | text, default `""` | Name contains: Part of the project's name. |
| `strictNameSearch` | true/false, default `false` | Match the name exactly |
| `archived` | "" or "true" or "false", default `""` | Archived |
| `billable` | "" or "true" or "false", default `""` | Billable |
| `clientIds` | text, default `""` | Client ids: Pick from this node's workspace, or type Clockify client ids. — a value from the vendor's own list |
| `userIds` | text, default `""` | User ids: Only projects these people are on. — a value from the vendor's own list |
| `access` | "" or "PUBLIC" or "PRIVATE", default `""` | Access |
| `hydrated` | true/false, default `false` | Include client, task and membership details |
| `sortColumn` | "" or "ID" or "NAME" or "CLIENT_NAME" or "DURATION" or "BUDGET" or "PROGRESS", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Projects per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 projects rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","name":"","strictNameSearch":false,"archived":"","billable":"","clientIds":"","userIds":"","access":"","hydrated":false,"sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"connection":"<connection id>"}
{"workspaceId":"","name":"","strictNameSearch":false,"archived":"","billable":"","clientIds":"","userIds":"","access":"","hydrated":false,"sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `clockify.listRunningTimers@2` — Clockify — List running timers

Shows every timer running right now in the Clockify workspace.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `pageSize` | number, default `10` | Timers per call: 1 to 1,000. Clockify's own default here is 10. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 timers rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `timeEntries` array — Running time entries · `timeEntryCount` number — Running timers in this answer · `hasMore` boolean — More timers are waiting behind this page · `nextPage` number — The page to ask for next, or 0 · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.listRunningTimers","version":2,"label":"List running timers","config":{"workspaceId":"","pageSize":10,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `clockify.listRunningTimers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `pageSize` | number, default `10` | Timers per call: 1 to 1,000. Clockify's own default here is 10. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 timers rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","pageSize":10,"returnAll":false,"connection":"<connection id>"}
{"workspaceId":"","pageSize":10,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `clockify.listTags@2` — Clockify — List tags

Lists the tags in the Clockify workspace.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `name` | text, default `""` | Name contains |
| `strictNameSearch` | true/false, default `false` | Match the name exactly |
| `excludedIds` | text, default `""` | Leave these out: Tag ids to skip. — a value from the vendor's own list |
| `archived` | "" or "true" or "false", default `""` | Archived |
| `sortColumn` | "" or "ID" or "NAME", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Tags per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 tags rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `tags` array — Tags · `tagCount` number — Tags in this answer · `hasMore` boolean — More tags are waiting behind this page · `nextPage` number — The page to ask for next, or 0 · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.listTags","version":2,"label":"List tags","config":{"workspaceId":"","name":"","strictNameSearch":false,"excludedIds":"","archived":"","sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `clockify.listTags@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `name` | text, default `""` | Name contains |
| `strictNameSearch` | true/false, default `false` | Match the name exactly |
| `excludedIds` | text, default `""` | Leave these out: Tag ids to skip. — a value from the vendor's own list |
| `archived` | "" or "true" or "false", default `""` | Archived |
| `sortColumn` | "" or "ID" or "NAME", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Tags per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 tags rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","name":"","strictNameSearch":false,"excludedIds":"","archived":"","sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"connection":"<connection id>"}
{"workspaceId":"","name":"","strictNameSearch":false,"excludedIds":"","archived":"","sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `clockify.listTasks@2` — Clockify — List tasks

Lists the tasks on one Clockify project.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `projectId` | text, required | Project id: The List projects node gives you the id. |
| `name` | text, default `""` | Name contains |
| `strictNameSearch` | true/false, default `false` | Match the name exactly |
| `isActive` | "" or "true" or "false", default `""` | Status |
| `sortColumn` | "" or "ID" or "NAME", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Tasks per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 tasks rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `tasks` array — Tasks · `taskCount` number — Tasks in this answer · `hasMore` boolean — More tasks are waiting behind this page · `nextPage` number — The page to ask for next, or 0 · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.listTasks","version":2,"label":"List tasks","config":{"workspaceId":"","name":"","strictNameSearch":false,"isActive":"","sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>","projectId":"<project id>"}}
```

### Older version `clockify.listTasks@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `projectId` | text, required | Project id: The List projects node gives you the id. |
| `name` | text, default `""` | Name contains |
| `strictNameSearch` | true/false, default `false` | Match the name exactly |
| `isActive` | "" or "true" or "false", default `""` | Status |
| `sortColumn` | "" or "ID" or "NAME", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Tasks per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 tasks rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","name":"","strictNameSearch":false,"isActive":"","sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"connection":"<connection id>","projectId":"<project id>"}
{"workspaceId":"","name":"","strictNameSearch":false,"isActive":"","sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>","projectId":"<project id>"}
```

## `clockify.listTimeEntries@2` — Clockify — List time entries

Lists one person's Clockify time entries in a date range.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `userId` | text, default `""` | User id: Empty means the person the API key belongs to — the List users node gives you an id. |
| `description` | text, default `""` | Description contains |
| `start` | text, default `""` | From |
| `end` | text, default `""` | To |
| `projectId` | text, default `""` | Project id |
| `taskId` | text, default `""` | Task id |
| `tagIds` | text, default `""` | Tag ids: Pick from this node's workspace, or type Clockify tag ids. — a value from the vendor's own list |
| `projectRequired` | true/false, default `false` | Only entries with a project |
| `taskRequired` | true/false, default `false` | Only entries with a task |
| `hydrated` | true/false, default `true` | Include project, task and tag details: Adds the project, task and tag objects instead of only their ids. |
| `inProgressOnly` | true/false, default `false` | Only running entries |
| `pageSize` | number, default `50` | Entries per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 entries rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `timeEntries` array — Time entries · `timeEntryCount` number — Time entries in this answer · `hasMore` boolean — More entries are waiting behind this page · `nextPage` number — The page to ask for next, or 0 · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.listTimeEntries","version":2,"label":"List time entries","config":{"workspaceId":"","userId":"","description":"","start":"","end":"","projectId":"","taskId":"","tagIds":"","projectRequired":false,"taskRequired":false,"hydrated":true,"inProgressOnly":false,"pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `clockify.listTimeEntries@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `userId` | text, default `""` | User id: Empty means the person the API key belongs to — the List users node gives you an id. |
| `description` | text, default `""` | Description contains |
| `start` | text, default `""` | From |
| `end` | text, default `""` | To |
| `projectId` | text, default `""` | Project id |
| `taskId` | text, default `""` | Task id |
| `tagIds` | text, default `""` | Tag ids: Pick from this node's workspace, or type Clockify tag ids. — a value from the vendor's own list |
| `projectRequired` | true/false, default `false` | Only entries with a project |
| `taskRequired` | true/false, default `false` | Only entries with a task |
| `hydrated` | true/false, default `true` | Include project, task and tag details: Adds the project, task and tag objects instead of only their ids. |
| `inProgressOnly` | true/false, default `false` | Only running entries |
| `pageSize` | number, default `50` | Entries per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 entries rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","userId":"","description":"","start":"","end":"","projectId":"","taskId":"","tagIds":"","projectRequired":false,"taskRequired":false,"hydrated":true,"inProgressOnly":false,"pageSize":50,"returnAll":false,"connection":"<connection id>"}
{"workspaceId":"","userId":"","description":"","start":"","end":"","projectId":"","taskId":"","tagIds":"","projectRequired":false,"taskRequired":false,"hydrated":true,"inProgressOnly":false,"pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `clockify.listTimeOffPolicies@2` — Clockify — List time off policies

Lists the Clockify time off policies and their ids.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `name` | text, default `""` | Name contains |
| `status` | "" or "ACTIVE" or "ARCHIVED" or "ALL", default `""` | Status |
| `sortColumn` | "" or "NAME", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Policies per call: 1 to 200 — that is Clockify's own maximum here. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 policies rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `policies` array — Policies · `policyCount` number — Policies in this answer · `hasMore` boolean — More policies are waiting behind this page · `nextPage` number — The page to ask for next, or 0 · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.listTimeOffPolicies","version":2,"label":"List time off policies","config":{"workspaceId":"","name":"","status":"","sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `clockify.listTimeOffPolicies@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `name` | text, default `""` | Name contains |
| `status` | "" or "ACTIVE" or "ARCHIVED" or "ALL", default `""` | Status |
| `sortColumn` | "" or "NAME", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | Policies per call: 1 to 200 — that is Clockify's own maximum here. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 policies rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","name":"","status":"","sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"connection":"<connection id>"}
{"workspaceId":"","name":"","status":"","sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `clockify.listTimeOffRequests@2` — Clockify — List time off requests

Lists Clockify time off requests in a period, by status and person.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `start` | text, default `""` | From |
| `end` | text, default `""` | To |
| `statuses` | list of "PENDING" or "APPROVED" or "REJECTED", default `[]` | Statuses: Leave all three unticked for every status. |
| `userIds` | text, default `""` | People: Pick from this node's workspace, or type Clockify user ids. — a value from the vendor's own list |
| `userGroupIds` | text, default `""` | User groups — a value from the vendor's own list |
| `pageSize` | number, default `50` | Requests per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 requests rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `requests` array — Time off requests · `requestCount` number — Requests in this answer · `hasMore` boolean — More requests are waiting behind this page · `nextPage` number — The page to ask for next, or 0 · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.listTimeOffRequests","version":2,"label":"List time off requests","config":{"workspaceId":"","start":"","end":"","statuses":[],"userIds":"","userGroupIds":"","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `clockify.listTimeOffRequests@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `start` | text, default `""` | From |
| `end` | text, default `""` | To |
| `statuses` | list of "PENDING" or "APPROVED" or "REJECTED", default `[]` | Statuses: Leave all three unticked for every status. |
| `userIds` | text, default `""` | People: Pick from this node's workspace, or type Clockify user ids. — a value from the vendor's own list |
| `userGroupIds` | text, default `""` | User groups — a value from the vendor's own list |
| `pageSize` | number, default `50` | Requests per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 requests rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","start":"","end":"","statuses":[],"userIds":"","userGroupIds":"","pageSize":50,"returnAll":false,"connection":"<connection id>"}
{"workspaceId":"","start":"","end":"","statuses":[],"userIds":"","userGroupIds":"","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `clockify.listUsers@2` — Clockify — List users

Lists the people in the Clockify workspace, with their status and rates.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `email` | text, default `""` | E-mail contains |
| `name` | text, default `""` | Name contains |
| `projectId` | text, default `""` | On this project: Only the people who are members of one project. |
| `status` | "" or "PENDING" or "ACTIVE" or "DECLINED" or "INACTIVE" or "ALL", default `""` | Status |
| `memberships` | "" or "ALL" or "NONE" or "WORKSPACE" or "PROJECT" or "USERGROUP", default `""` | Include memberships |
| `includeRoles` | true/false, default `false` | Include roles: Adds each person's manager roles to the answer. |
| `sortColumn` | "" or "ID" or "EMAIL" or "NAME" or "NAME_LOWERCASE" or "ACCESS" or "HOURLYRATE" or "COSTRATE", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | People per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 people rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `users` array — People · `userCount` number — People in this answer · `hasMore` boolean — More people are waiting behind this page · `nextPage` number — The page to ask for next, or 0 · `nextCursor` string — Cursor for the next page.

```json
{"type":"clockify.listUsers","version":2,"label":"List users","config":{"workspaceId":"","email":"","name":"","projectId":"","status":"","memberships":"","includeRoles":false,"sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `clockify.listUsers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `email` | text, default `""` | E-mail contains |
| `name` | text, default `""` | Name contains |
| `projectId` | text, default `""` | On this project: Only the people who are members of one project. |
| `status` | "" or "PENDING" or "ACTIVE" or "DECLINED" or "INACTIVE" or "ALL", default `""` | Status |
| `memberships` | "" or "ALL" or "NONE" or "WORKSPACE" or "PROJECT" or "USERGROUP", default `""` | Include memberships |
| `includeRoles` | true/false, default `false` | Include roles: Adds each person's manager roles to the answer. |
| `sortColumn` | "" or "ID" or "EMAIL" or "NAME" or "NAME_LOWERCASE" or "ACCESS" or "HOURLYRATE" or "COSTRATE", default `""` | Sort by |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `pageSize` | number, default `50` | People per call: 1 to 200. Clockify's own default is 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Clockify's pages, and refuses past 5,000 people rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","email":"","name":"","projectId":"","status":"","memberships":"","includeRoles":false,"sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"connection":"<connection id>"}
{"workspaceId":"","email":"","name":"","projectId":"","status":"","memberships":"","includeRoles":false,"sortColumn":"","sortOrder":"ASCENDING","pageSize":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `clockify.listWorkspaces@1` — Clockify — List workspaces

Shows which Clockify workspaces this key can reach, and their ids.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `roles` | "" or "WORKSPACE_ADMIN" or "OWNER" or "TEAM_MANAGER" or "PROJECT_MANAGER", default `""` | Only where the key's user is |

Output: `workspaces` array — Workspaces · `workspaceCount` number — Workspaces in this answer.

```json
{"type":"clockify.listWorkspaces","version":1,"label":"List workspaces","config":{"roles":"","connection":"<connection id>"}}
```

## `clockify.setTimeOffRequestStatus@1` — Clockify — Approve or reject time off

Approves or rejects a Clockify time off request.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `policyId` | text, required | Policy id: The policy the request was filed under. |
| `requestId` | text, required | Request id: The List time off requests node gives you the id. |
| `status` | "APPROVED" or "REJECTED", default `"APPROVED"` | Decision |
| `note` | text, default `""` | Note: What the person reads with the decision. |

Output: `requestId` string — Request id · `request` object — Time off request.

```json
{"type":"clockify.setTimeOffRequestStatus","version":1,"label":"Approve or reject time off","config":{"workspaceId":"","status":"APPROVED","note":"","connection":"<connection id>","policyId":"<policy id>","requestId":"<request id>"}}
```

## `clockify.stopTimer@1` — Clockify — Stop timer

Stops the timer that is running for a person in Clockify.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `userId` | text, default `""` | User id: Empty stops the timer of the person the API key belongs to — the List users node gives you an id. |
| `end` | text, default `""` | Stop at: Leave empty to stop the timer at the moment this step runs. |

Output: `stopped` boolean — A timer was running and is now stopped · `timeEntryId` string — Time entry id · `timeEntry` any — Time entry, or nothing when no timer was running.

```json
{"type":"clockify.stopTimer","version":1,"label":"Stop timer","config":{"workspaceId":"","userId":"","end":"","connection":"<connection id>"}}
```

## `clockify.summaryReport@1` — Clockify — Summary report

Runs Clockify's summary report, grouped the way you choose.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `dateRangeType` | "ABSOLUTE" or "TODAY" or "YESTERDAY" or "THIS_WEEK" or "LAST_WEEK" or "PAST_TWO_WEEKS" or "THIS_MONTH" or "LAST_MONTH" or "THIS_YEAR" or "LAST_YEAR", default `"LAST_MONTH"` | Period |
| `dateRangeStart` | text, default `""` | From — used when `dateRangeType` is "ABSOLUTE" |
| `dateRangeEnd` | text, default `""` | To — used when `dateRangeType` is "ABSOLUTE" |
| `exportType` | "JSON" or "CSV" or "XLSX" or "PDF" or "ZIP", default `"JSON"` | Answer as: Rows come back as data you can loop over. Every other choice comes back as a file — which shape Clockify sends that file in is owed one run against a real workspace. |
| `groups` | list of {group: text}, default `[{"group":"PROJECT"}]` | Group by: The grouping chain, in order — Project then Person gives a row per project with its people underneath. |
| `summaryChartType` | "" or "BILLABILITY" or "PROJECT", default `""` | Chart |
| `sortColumn` | "GROUP" or "DURATION" or "AMOUNT" or "EARNED" or "COST" or "PROFIT", default `"GROUP"` | Sort by |
| `userIds` | text, default `""` | People — a value from the vendor's own list |
| `projectIds` | text, default `""` | Projects — a value from the vendor's own list |
| `clientIds` | text, default `""` | Clients — a value from the vendor's own list |
| `taskIds` | text, default `""` | Tasks — a value from the vendor's own list |
| `tagIds` | text, default `""` | Tags — a value from the vendor's own list |
| `billable` | "" or "true" or "false", default `""` | Billable |
| `approvalState` | "ALL" or "APPROVED" or "UNAPPROVED", default `"ALL"` | Approval |
| `invoicingState` | "ALL" or "INVOICED" or "UNINVOICED", default `"ALL"` | Invoicing |
| `description` | text, default `""` | Description contains |
| `withoutDescription` | true/false, default `false` | Only entries with no description |
| `rounding` | true/false, default `false` | Round the way the workspace does |
| `amountShown` | "HIDE_AMOUNT" or "EARNED" or "COST" or "PROFIT" or "EXPORT", default `"HIDE_AMOUNT"` | Amounts |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `timeZone` | text, default `""` | Time zone |
| `weekStart` | "MONDAY" or "TUESDAY" or "WEDNESDAY" or "THURSDAY" or "FRIDAY" or "SATURDAY" or "SUNDAY", default `"MONDAY"` | Week starts on |
| `filter` | object (any keys), default `{}` | Anything else: Anything else from Clockify's report filter, merged over the fields above. The fields above win only where this leaves a key out. |

Output: `groupOne` array — Groups · `totals` array — Totals · `file` any — Report file, or nothing when the answer is rows · `file.base64` string — Report file (base64) · `file.name` string — File name · `file.contentType` string — Content type · `file.size` number — Size in bytes.

```json
{"type":"clockify.summaryReport","version":1,"label":"Summary report","config":{"workspaceId":"","dateRangeType":"LAST_MONTH","dateRangeStart":"","dateRangeEnd":"","exportType":"JSON","groups":[{"group":"PROJECT"}],"summaryChartType":"","sortColumn":"GROUP","userIds":"","projectIds":"","clientIds":"","taskIds":"","tagIds":"","billable":"","approvalState":"ALL","invoicingState":"ALL","description":"","withoutDescription":false,"rounding":false,"amountShown":"HIDE_AMOUNT","sortOrder":"ASCENDING","timeZone":"","weekStart":"MONDAY","filter":{},"connection":"<connection id>"}}
```

## `clockify.updateProject@2` — Clockify — Update project

Changes or archives a Clockify project.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `projectId` | text, required | Project id: The List projects node gives you the id. |
| `name` | text, default `""` | Project name: Leave empty to keep the name it has. |
| `clientId` | text, default `""` | Client id |
| `color` | text, default `""` | Colour: Six hex digits after a hash. Leave empty to keep the colour it has. |
| `note` | text, default `""` | Note |
| `billable` | "" or "on" or "off", default `""` | Billable |
| `isPublic` | "" or "on" or "off", default `""` | Who can see it |
| `archived` | "" or "on" or "off", default `""` | Archived: An archived project keeps its time entries, but nobody can log new time on it. |

Output: `projectId` string — Project id · `project` object — Project.

```json
{"type":"clockify.updateProject","version":2,"label":"Update project","config":{"workspaceId":"","name":"","clientId":"","color":"","note":"","billable":"","isPublic":"","archived":"","connection":"<connection id>","projectId":"<project id>"}}
```

### Older version `clockify.updateProject@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. — a value from the vendor's own list |
| `projectId` | text, required | Project id: The List projects node gives you the id. |
| `name` | text, default `""` | Project name: Leave empty to keep the name it has. |
| `clientId` | text, default `""` | Client id |
| `color` | text, default `""` | Colour: Six hex digits after a hash. Leave empty to keep the colour it has. |
| `note` | text, default `""` | Note |
| `billable` | true/false, default `true` | Billable |
| `isPublic` | true/false, default `true` | Everyone in the workspace can see it |
| `archived` | true/false, default `false` | Archive it: An archived project keeps its time entries, but nobody can log new time on it. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","name":"","clientId":"","color":"","note":"","billable":true,"isPublic":true,"archived":false,"connection":"<connection id>","projectId":"<project id>"}
{"workspaceId":"","name":"","clientId":"","color":"","note":"","billable":true,"isPublic":true,"archived":false,"connection":"<connection id>","projectId":"<project id>"}
```

## `clockify.updateTask@2` — Clockify — Update task

Changes a task on a Clockify project, or marks it done.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `projectId` | text, required | Project id |
| `taskId` | text, required | Task id: The List tasks node gives you the id. |
| `name` | text, required | Task name: Clockify wants the name on every update, even when you are only changing the status. Read the task first if you do not have it. |
| `assigneeIds` | text, default `""` | Assignees: Leaving this empty keeps the people already on the task. — a value from the vendor's own list |
| `estimate` | text, default `""` | Estimated time: An ISO 8601 duration — PT8H is eight hours. |
| `billable` | "" or "on" or "off", default `""` | Billable |
| `status` | "" or "ACTIVE" or "DONE", default `""` | Status |

Output: `taskId` string — Task id · `task` object — Task.

```json
{"type":"clockify.updateTask","version":2,"label":"Update task","config":{"workspaceId":"","assigneeIds":"","estimate":"","billable":"","status":"","connection":"<connection id>","projectId":"<project id>","taskId":"<task id>","name":"<task name>"}}
```

### Older version `clockify.updateTask@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `projectId` | text, required | Project id |
| `taskId` | text, required | Task id: The List tasks node gives you the id. |
| `name` | text, required | Task name: Clockify wants the name on every update, even when you are only changing the status. Read the task first if you do not have it. |
| `assigneeIds` | text, default `""` | Assignees: Leaving this empty keeps the people already on the task. — a value from the vendor's own list |
| `estimate` | text, default `""` | Estimated time: An ISO 8601 duration — PT8H is eight hours. |
| `billable` | true/false, default `true` | Billable |
| `status` | "ACTIVE" or "DONE", default `"ACTIVE"` | Status |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceId":"","assigneeIds":"","estimate":"","billable":true,"status":"ACTIVE","connection":"<connection id>","projectId":"<project id>","taskId":"<task id>","name":"<task name>"}
{"workspaceId":"","assigneeIds":"","estimate":"","billable":true,"status":"ACTIVE","connection":"<connection id>","projectId":"<project id>","taskId":"<task id>","name":"<task name>"}
```

## `clockify.updateTimeEntry@1` — Clockify — Update time entry

Changes a Clockify time entry you already logged.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `timeEntryId` | text, required | Time entry id: The entry to replace — the List time entries node gives you the id. |
| `start` | text, default `""` | Start: Clockify replaces the whole entry, so it wants the start even when you only change the description. |
| `end` | text, default `""` | End: Leave empty to leave the entry running. |
| `description` | text, default `""` | Description: Up to 3,000 characters. Leaving it empty clears the description. |
| `projectId` | text, default `""` | Project id |
| `taskId` | text, default `""` | Task id |
| `tagIds` | text, default `""` | Tag ids — a value from the vendor's own list |
| `billable` | true/false, default `false` | Billable |
| `type` | "REGULAR" or "BREAK", default `"REGULAR"` | Kind |
| `customFields` | object of text, default `{}` | Custom fields: Keyed by the custom field's id — the List custom fields node gives you those. |

Output: `timeEntryId` string — Time entry id · `timeEntry` object — Time entry.

```json
{"type":"clockify.updateTimeEntry","version":1,"label":"Update time entry","config":{"workspaceId":"","start":"","end":"","description":"","projectId":"","taskId":"","tagIds":"","billable":false,"type":"REGULAR","customFields":{},"connection":"<connection id>","timeEntryId":"<time entry id>"}}
```

## `clockify.weeklyReport@1` — Clockify — Weekly report

Runs Clockify's weekly report for a week you choose.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `clockify`) |
| `workspaceId` | text, default `""` | Workspace id override: Leave empty to use the workspace on the connection. Pick another workspace the same key can reach, or type its id — the List workspaces node gives you the ids. The id pickers on this node list from it. — a value from the vendor's own list |
| `dateRangeType` | "ABSOLUTE" or "TODAY" or "YESTERDAY" or "THIS_WEEK" or "LAST_WEEK" or "PAST_TWO_WEEKS" or "THIS_MONTH" or "LAST_MONTH" or "THIS_YEAR" or "LAST_YEAR", default `"THIS_WEEK"` | Period |
| `dateRangeStart` | text, default `""` | From — used when `dateRangeType` is "ABSOLUTE" |
| `dateRangeEnd` | text, default `""` | To — used when `dateRangeType` is "ABSOLUTE" |
| `exportType` | "JSON" or "CSV" or "XLSX" or "PDF" or "ZIP", default `"JSON"` | Answer as: Rows come back as data you can loop over. Every other choice comes back as a file — which shape Clockify sends that file in is owed one run against a real workspace. |
| `group` | text, default `"USER"` | Rows: What each row of the grid is. Clockify publishes no list of allowed values, so anything it accepts is accepted here. |
| `subgroup` | text, default `"PROJECT"` | Rows underneath: What the second level of the grid is. |
| `userIds` | text, default `""` | People — a value from the vendor's own list |
| `projectIds` | text, default `""` | Projects — a value from the vendor's own list |
| `clientIds` | text, default `""` | Clients — a value from the vendor's own list |
| `taskIds` | text, default `""` | Tasks — a value from the vendor's own list |
| `tagIds` | text, default `""` | Tags — a value from the vendor's own list |
| `billable` | "" or "true" or "false", default `""` | Billable |
| `approvalState` | "ALL" or "APPROVED" or "UNAPPROVED", default `"ALL"` | Approval |
| `invoicingState` | "ALL" or "INVOICED" or "UNINVOICED", default `"ALL"` | Invoicing |
| `description` | text, default `""` | Description contains |
| `withoutDescription` | true/false, default `false` | Only entries with no description |
| `rounding` | true/false, default `false` | Round the way the workspace does |
| `amountShown` | "HIDE_AMOUNT" or "EARNED" or "COST" or "PROFIT" or "EXPORT", default `"HIDE_AMOUNT"` | Amounts |
| `sortOrder` | "ASCENDING" or "DESCENDING", default `"ASCENDING"` | Direction |
| `timeZone` | text, default `""` | Time zone |
| `weekStart` | "MONDAY" or "TUESDAY" or "WEDNESDAY" or "THURSDAY" or "FRIDAY" or "SATURDAY" or "SUNDAY", default `"MONDAY"` | Week starts on |
| `filter` | object (any keys), default `{}` | Anything else: Anything else from Clockify's report filter, merged over the fields above. The fields above win only where this leaves a key out. |

Output: `weeks` array — Grid rows, one per group · `totals` array — Totals · `totalsByDay` array — Totals per day · `file` any — Report file, or nothing when the answer is rows · `file.base64` string — Report file (base64) · `file.name` string — File name · `file.contentType` string — Content type · `file.size` number — Size in bytes.

```json
{"type":"clockify.weeklyReport","version":1,"label":"Weekly report","config":{"workspaceId":"","dateRangeType":"THIS_WEEK","dateRangeStart":"","dateRangeEnd":"","exportType":"JSON","group":"USER","subgroup":"PROJECT","userIds":"","projectIds":"","clientIds":"","taskIds":"","tagIds":"","billable":"","approvalState":"ALL","invoicingState":"ALL","description":"","withoutDescription":false,"rounding":false,"amountShown":"HIDE_AMOUNT","sortOrder":"ASCENDING","timeZone":"","weekStart":"MONDAY","filter":{},"connection":"<connection id>"}}
```


---

## File: references/nodes/core.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Core nodes (`core.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `core.break@1` — Break

Stops the loop it is in; the flow goes on after the loop.

Ports: none — no edge may leave it · changes nothing outside the run · on-error setting must stay `fail`

Config: `{}` (no fields).

Output: depends on the data.

```json
{"type":"core.break","version":1,"label":"Break","config":{}}
```

## `core.callFlow@2` — Subflow

Calls another flow with the values you pass, and continues with its result.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `flowId` | text, required | Flow: Every flow but this one is offered, except those that would loop back. A test run calls its test version, a production run its production version. — a flow id from `requires.flows` |
| `mode` | "wait" or "detach", default `"wait"` | When it runs: Waiting hands the called flow's result to the next step. Starting it hands on its run id, and the called flow runs on its own. |
| `params` | object (any keys), default `{}` | Parameters: One value per parameter the called flow declares; a value can come from an earlier step. Leave a row empty to use its default. |
| `editorRuns` | "version" or "draft", default `"version"` | In editor runs, call: When you press Execute here: call the called flow's published test version, or its draft as it is saved now, with its pins — so you can try both flows together before publishing. Test and production runs always call its published version. |

Output: shaped by the config.

```json
{"type":"core.callFlow","version":2,"label":"Subflow","config":{"mode":"wait","params":{},"editorRuns":"version","flowId":"<flow>"}}
```

### Older version `core.callFlow@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `flowId` | text, required | Flow: Every flow but this one is offered, except those that would loop back. A test run calls its test version, a production run its production version. — a flow id from `requires.flows` |
| `mode` | "wait" or "detach", default `"wait"` | When it runs: Waiting hands the called flow's result to the next step. Starting it hands on its run id, and the called flow runs on its own. |
| `params` | object (any keys), default `{}` | Parameters: One value per parameter the called flow declares; a value can come from an earlier step. Leave a row empty to use its default. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"mode":"wait","params":{},"flowId":"<flow>"}
{"mode":"wait","params":{},"editorRuns":"version","flowId":"<flow>"}
```

## `core.condition@1` — Condition

Chooses between two paths, one when your check is true, one when it is false.

Ports: `true`, `false` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `expression` | text, required | Rules: Must evaluate to a real boolean — use json.x != null for existence checks, and wrap a comparison in {{ }}. — its `{{ }}` must give a boolean |

Output: depends on the data.

```json
{"type":"core.condition","version":1,"label":"Condition","config":{"expression":"<rules>"}}
```

## `core.delay@1` — Wait

Waits before the flow continues — for a while, or until a moment you name.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `mode` | "interval" or "until", default `"interval"` | Wait |
| `amount` | number, optional | How long: A number, or a value from an earlier step that is one. — used when `mode` is not "until" |
| `unit` | "seconds" or "minutes" or "hours" or "days", default `"minutes"` | Unit: Up to 31 days in total. — used when `mode` is not "until" |
| `until` | text, optional | Moment: An ISO 8601 moment, at most 31 days away. — its `{{ }}` must give a string; used when `mode` is "until" |

Output: depends on the data.

```json
{"type":"core.delay","version":1,"label":"Wait","config":{"mode":"interval","unit":"minutes","amount":1}}
```

## `core.http@2` — HTTP request

Calls any web API and brings its answer into the flow, page after page if needed.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `method` | "GET" or "POST" or "PUT" or "PATCH" or "DELETE", default `"GET"` | Method |
| `url` | text, required | URL |
| `query` | object of text, default `{}` | Query parameters: Appended to the URL and encoded; a value can come from an earlier step. |
| `headers` | object of text or number or true/false, default `{}` | Headers: A value can come from an earlier step. Credentials belong in Sign in with above, not in a typed header. |
| `body` | any JSON, optional | Body: Text with {{ }} expressions; a whole-value expression that yields an object is sent as JSON — or {{ node["Build body"] }} from a json.build node. — used when `method` is not "GET" |
| `bodyEncoding` | "text" or "base64", default `"text"` | Body encoding: Base64 is decoded to bytes before the request leaves. — used when `method` is not "GET" |
| `responseFormat` | "auto" or "text" or "base64" or "file", default `"auto"` | Response format: Base64 keeps a PDF or an image intact for file.write — set that node's Encoding to Base64 too, or it writes the characters instead of the bytes. An answer too large to read at once fails the step — read it in pages, or save it as a file. |
| `fileName` | text, default `""` | File name: The workspace file the answer is saved to, up to 100 MB — saving again adds a version. The step answers the file's name, size and hash, never its bytes; read it with file.read. — used when `responseFormat` is "file" |
| `followRedirects` | true/false, default `true` | Follow redirects: Switch off to read a 3xx and its Location header as data. |
| `auth.kind` | "none" or "bearer" or "basic" or "header" or "query" or "connection", default `"none"` | Sign in with: Composed here and never logged — the value is redacted in every view. |
| `auth.headerName` | text, default `"Authorization"` | Header name — used when `auth.kind` is "header" |
| `auth.queryName` | text, default `"access_token"` | Parameter name — used when `auth.kind` is "query" |
| `auth.token` | text, optional | Token — write `{{ secret.NAME }}`, never the value; used when `auth.kind` is one of "bearer", "header", "query" |
| `auth.username` | text, optional | User — write `{{ secret.NAME }}`, never the value; used when `auth.kind` is "basic" |
| `auth.password` | text, optional | Password — write `{{ secret.NAME }}`, never the value; used when `auth.kind` is "basic" |
| `pagination.mode` | "off" or "nextLink" or "pageNumber" or "offsetLimit" or "cursor", default `"off"` | Pages: Walks the pages for you and returns every item in one list. Every page is one request of the same kind as this one, and the node refuses rather than hand you half a list. |
| `pagination.linkFrom` | "body" or "header", default `"body"` | Where is the link?: Most APIs put the next page's address in the answer; some send it in the Link header instead. — used when `pagination.mode` is "nextLink" |
| `pagination.linkPath` | text, default `""` | Field path: The name of the field holding the next page's address, dots for the steps in between — read only when the link is in the response. — used when `pagination.mode` is "nextLink" |
| `pagination.pageParam` | text, default `"page"` | Parameter name: The query parameter this API counts pages in. The node stops at the first page with no items. — used when `pagination.mode` is "pageNumber" |
| `pagination.firstPage` | number, default `1` | First page: Most APIs count from 1; some count from 0. — used when `pagination.mode` is "pageNumber" |
| `pagination.offsetParam` | text, default `"offset"` | Offset parameter: The query parameter that says how many items to pass over. The node stops when a page answers fewer items than the page size. — used when `pagination.mode` is "offsetLimit" |
| `pagination.limitParam` | text, default `"limit"` | Limit parameter: The query parameter that asks for a page size. — used when `pagination.mode` is "offsetLimit" |
| `pagination.pageSize` | number, default `100` | Page size: How many items to ask for per request — sent as the limit. — used when `pagination.mode` is "offsetLimit" |
| `pagination.cursorPath` | text, default `"meta.next_cursor"` | Cursor is in the response at: The name of the field holding the token for the next page. The node stops when it is missing or empty. — used when `pagination.mode` is "cursor" |
| `pagination.cursorParam` | text, default `"cursor"` | Send it as parameter: The query parameter the token travels back in. — used when `pagination.mode` is "cursor" |
| `pagination.itemsPath` | text, default `""` | Items are in: The name of the field holding the list in each answer, dots for the steps in between — leave it empty when the answer IS the list. — used when `pagination.mode` is one of "nextLink", "pageNumber", "offsetLimit", "cursor" |
| `pagination.maxPages` | number, default `20` | Stop after: Pages — at most 100. If the API still has more when this is reached, the node fails and says so instead of handing you a short list. — used when `pagination.mode` is one of "nextLink", "pageNumber", "offsetLimit", "cursor" |
| `connection` | text, default `""` | Connection: Any connection of this workspace — its own strategy composes the header, so this node never holds a credential. — a connection id from `requires.connections` (any provider), or empty; used when `auth.kind` is "connection" |

Output: `status` number — Status code · `headers` object — Response headers · `body` any — Response body · `bodyEncoding` string — Body encoding — text, base64 or file · `bodySizeBytes` number — Response body size (bytes) · `pages` number — Pages fetched (only with pagination on).

```json
{"type":"core.http","version":2,"label":"HTTP request","config":{"method":"GET","query":{},"headers":{},"bodyEncoding":"text","responseFormat":"auto","fileName":"","followRedirects":true,"auth":{"kind":"none","headerName":"Authorization","queryName":"access_token"},"pagination":{"mode":"off","linkFrom":"body","linkPath":"","pageParam":"page","firstPage":1,"offsetParam":"offset","limitParam":"limit","pageSize":100,"cursorPath":"meta.next_cursor","cursorParam":"cursor","itemsPath":"","maxPages":20},"connection":"","url":"<url>"}}
```

### Older version `core.http@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `method` | "GET" or "POST" or "PUT" or "PATCH" or "DELETE", default `"GET"` | Method |
| `url` | text, required | URL |
| `query` | object of text, default `{}` | Query parameters: Appended to the URL and encoded; a value can come from an earlier step. |
| `headers` | object of text or number or true/false, default `{}` | Headers: A value can come from an earlier step. Credentials belong in Sign in with above, not in a typed header. |
| `body` | any JSON, optional | Body: Text with {{ }} expressions; a whole-value expression that yields an object is sent as JSON — or {{ node["Build body"] }} from a json.build node. — used when `method` is not "GET" |
| `bodyEncoding` | "text" or "base64", default `"text"` | Body encoding: Base64 is decoded to bytes before the request leaves. — used when `method` is not "GET" |
| `responseFormat` | "auto" or "text" or "base64" or "file", default `"auto"` | Response format: Base64 keeps a PDF or an image intact for file.write — set that node's Encoding to Base64 too, or it writes the characters instead of the bytes. An answer too large to read at once fails the step — read it in pages, or save it as a file. |
| `fileName` | text, default `""` | File name: The workspace file the answer is saved to, up to 100 MB — saving again adds a version. The step answers the file's name, size and hash, never its bytes; read it with file.read. — used when `responseFormat` is "file" |
| `followRedirects` | true/false, default `true` | Follow redirects: Switch off to read a 3xx and its Location header as data. |
| `auth.kind` | "none" or "bearer" or "basic" or "header" or "query", default `"none"` | Sign in with: Composed here and never logged — the value is redacted in every view. |
| `auth.headerName` | text, default `"Authorization"` | Header name — used when `auth.kind` is "header" |
| `auth.queryName` | text, default `"access_token"` | Parameter name — used when `auth.kind` is "query" |
| `auth.token` | text, optional | Token — write `{{ secret.NAME }}`, never the value; used when `auth.kind` is one of "bearer", "header", "query" |
| `auth.username` | text, optional | User — write `{{ secret.NAME }}`, never the value; used when `auth.kind` is "basic" |
| `auth.password` | text, optional | Password — write `{{ secret.NAME }}`, never the value; used when `auth.kind` is "basic" |
| `pagination.mode` | "off" or "nextLink" or "pageNumber" or "offsetLimit" or "cursor", default `"off"` | Pages: Walks the pages for you and returns every item in one list. Every page is one request of the same kind as this one, and the node refuses rather than hand you half a list. |
| `pagination.linkFrom` | "body" or "header", default `"body"` | Where is the link?: Most APIs put the next page's address in the answer; some send it in the Link header instead. — used when `pagination.mode` is "nextLink" |
| `pagination.linkPath` | text, default `""` | Field path: The name of the field holding the next page's address, dots for the steps in between — read only when the link is in the response. — used when `pagination.mode` is "nextLink" |
| `pagination.pageParam` | text, default `"page"` | Parameter name: The query parameter this API counts pages in. The node stops at the first page with no items. — used when `pagination.mode` is "pageNumber" |
| `pagination.firstPage` | number, default `1` | First page: Most APIs count from 1; some count from 0. — used when `pagination.mode` is "pageNumber" |
| `pagination.offsetParam` | text, default `"offset"` | Offset parameter: The query parameter that says how many items to pass over. The node stops when a page answers fewer items than the page size. — used when `pagination.mode` is "offsetLimit" |
| `pagination.limitParam` | text, default `"limit"` | Limit parameter: The query parameter that asks for a page size. — used when `pagination.mode` is "offsetLimit" |
| `pagination.pageSize` | number, default `100` | Page size: How many items to ask for per request — sent as the limit. — used when `pagination.mode` is "offsetLimit" |
| `pagination.cursorPath` | text, default `"meta.next_cursor"` | Cursor is in the response at: The name of the field holding the token for the next page. The node stops when it is missing or empty. — used when `pagination.mode` is "cursor" |
| `pagination.cursorParam` | text, default `"cursor"` | Send it as parameter: The query parameter the token travels back in. — used when `pagination.mode` is "cursor" |
| `pagination.itemsPath` | text, default `""` | Items are in: The name of the field holding the list in each answer, dots for the steps in between — leave it empty when the answer IS the list. — used when `pagination.mode` is one of "nextLink", "pageNumber", "offsetLimit", "cursor" |
| `pagination.maxPages` | number, default `20` | Stop after: Pages — at most 100. If the API still has more when this is reached, the node fails and says so instead of handing you a short list. — used when `pagination.mode` is one of "nextLink", "pageNumber", "offsetLimit", "cursor" |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"method":"GET","query":{},"headers":{},"bodyEncoding":"text","responseFormat":"auto","fileName":"","followRedirects":true,"auth":{"kind":"none","headerName":"Authorization","queryName":"access_token"},"pagination":{"mode":"off","linkFrom":"body","linkPath":"","pageParam":"page","firstPage":1,"offsetParam":"offset","limitParam":"limit","pageSize":100,"cursorPath":"meta.next_cursor","cursorParam":"cursor","itemsPath":"","maxPages":20},"url":"<url>"}
{"method":"GET","query":{},"headers":{},"bodyEncoding":"text","responseFormat":"auto","fileName":"","followRedirects":true,"auth":{"kind":"none","headerName":"Authorization","queryName":"access_token"},"pagination":{"mode":"off","linkFrom":"body","linkPath":"","pageParam":"page","firstPage":1,"offsetParam":"offset","limitParam":"limit","pageSize":100,"cursorPath":"meta.next_cursor","cursorParam":"cursor","itemsPath":"","maxPages":20},"connection":"","url":"<url>"}
```

## `core.javascript@1` — JavaScript

Runs your own JavaScript when expressions cannot do it — map, filter, regex, custom logic.

Ports: `out` · changes nothing outside the run

Config: `{}` (no fields).

Node key `code` (beside `config`, never inside it), required: Use paths without `{{ }}` here: `input.email`, not `{{ json.email }}`. `input` is the previous step's output; what you `return` becomes this step's output. Your code is the body of `async function run(input)`. `json` is the same value as `input`, and `trigger`, `loop`, `execution`, `flow`, `workspace`, `params` and `$now` are plain values here too — `params` is a copy of the parameters this run started with — changing it changes nothing outside your code, a name the flow does not declare is `undefined`, and your own `params` variable takes its place. `node` holds the earlier steps you name in the code — `node["Step label"]` or `node.stepId` — and a step that did not run is `null`. `secret` is not available in code, and `{{ }}` is never interpreted here — it is JavaScript. No network (use HTTP request), and 4 MiB in and out.

Output: depends on the data.

```json
{"type":"core.javascript","version":1,"label":"JavaScript","config":{},"code":"return input;"}
```

## `core.log@1` — Log

Writes a line to the run's log and passes the data on unchanged.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `message` | text, default `""` | Message: The line the run log shows for this step. |
| `data` | any JSON, optional | Data: Shown under the line, first 4 KB, secrets masked. Empty shows this step's input. The next step always gets the input, whatever is shown here. |
| `level` | "info" or "warn" or "error", default `"info"` | Level: An error line is only a line: the run carries on. |

Output: depends on the data.

```json
{"type":"core.log","version":1,"label":"Log","config":{"message":"","level":"info"}}
```

## `core.loop@3` — Loop

Repeats the steps you connect once for every item in a list.

Ports: `body`, `done` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `items` | text, required | Items: Must evaluate to a list — evaluated once, when the loop starts. Everything you connect to the loop's body output runs once per item and the chain ends where it stops; never connect it back to this node. Very large lists are capped per workspace: loop over IDs and read each record inside the body, or fetch the list in smaller batches (most services take a limit or page setting), instead of carrying the whole dataset through the loop. — its `{{ }}` must give an array |
| `collect` | text, default `""` | Collect: Optional. Read one value from each round — evaluated after the round ends, against that round — and the loop's done output lists them in order as collected. Name a step inside the loop, {{ node["Enrich"] }}, or one field of it, {{ node["Enrich"].total }}; for a loop inside this one, collect in that loop and name its list, {{ node["Inner"].collected }}. Secrets, files, schemas and json cannot be read here. The list is kept whole: if it grows past what one step's output may hold, the loop fails instead of cutting it short. |
| `allPages` | true/false, default `false` | Read every page: Read every page of a list step that takes a cursor, like HubSpot — List records, or Table — Select returning its first rows: name its list as the items, {{ json.records }}, and the loop asks that step for the next page each time it runs out, until the list ends. Leave the step's own Fetch every page off. A step that cannot page is refused when you deploy. |

Output: `itemCount` number (port `done`) — Item count · `completedCount` number (port `done`) — Completed count · more fields follow from the config.

```json
{"type":"core.loop","version":3,"label":"Loop","config":{"collect":"","allPages":false,"items":"<items>"}}
```

### Older version `core.loop@1` — still runs; upgrade it to @3

| config key | value | meaning |
|---|---|---|
| `items` | text, required | Items: Must evaluate to a list — evaluated once, when the loop starts. Everything you connect to the loop's body output runs once per item and the chain ends where it stops; never connect it back to this node. Very large lists are capped per workspace: loop over IDs and read each record inside the body, or fetch the list in smaller batches (most services take a limit or page setting), instead of carrying the whole dataset through the loop. — its `{{ }}` must give an array |

Upgrade @1 → @3: set `version` to 3 and replace the config — before, then after:
```json
{"items":"<items>"}
{"collect":"","allPages":false,"items":"<items>"}
```

### Older version `core.loop@2` — still runs; upgrade it to @3

| config key | value | meaning |
|---|---|---|
| `items` | text, required | Items: Must evaluate to a list — evaluated once, when the loop starts. Everything you connect to the loop's body output runs once per item and the chain ends where it stops; never connect it back to this node. Very large lists are capped per workspace: loop over IDs and read each record inside the body, or fetch the list in smaller batches (most services take a limit or page setting), instead of carrying the whole dataset through the loop. — its `{{ }}` must give an array |
| `collect` | text, default `""` | Collect: Optional. Read one value from each round — evaluated after the round ends, against that round — and the loop's done output lists them in order as collected. Name a step inside the loop, {{ node["Enrich"] }}, or one field of it, {{ node["Enrich"].total }}; for a loop inside this one, collect in that loop and name its list, {{ node["Inner"].collected }}. Secrets, files, schemas and json cannot be read here. The list is kept whole: if it grows past what one step's output may hold, the loop fails instead of cutting it short. |

Upgrade @2 → @3: set `version` to 3 and replace the config — before, then after:
```json
{"collect":"","items":"<items>"}
{"collect":"","allPages":false,"items":"<items>"}
```

## `core.raiseError@1` — Raise error

Stops the flow with an error message you write.

Ports: `out` · changes nothing outside the run · on-error setting must stay `fail`

| config key | value | meaning |
|---|---|---|
| `message` | text, required | Error message |
| `code` | text, default `"FLOW_ERROR"` | Error code: Shows up in the runs list, the error mail and json.error.code downstream. Defaults to FLOW_ERROR. |

Output: depends on the data.

```json
{"type":"core.raiseError","version":1,"label":"Raise error","config":{"code":"FLOW_ERROR","message":"<error message>"}}
```

## `core.return@1` — Return

Ends the flow here and hands back a value as its result.

Ports: none — no edge may leave it · changes nothing outside the run · on-error setting must stay `fail`

| config key | value | meaning |
|---|---|---|
| `value` | any JSON, optional | Result: What this flow answers with — to the flow that called it, or to a webhook waiting for a response. Leave it empty to answer with this step's input. Every step that has not run yet is skipped. |

Output: depends on the data.

```json
{"type":"core.return","version":1,"label":"Return","config":{}}
```

## `core.switch@2` — Switch

Routes the flow to one of many paths, by the value it matches.

Ports: one per row `id` in `cases`, plus `else` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `expression` | text, required | Value to look at: Compared to each case's value exactly — a number never matches text. |
| `cases` | list of {id: text, label?: text, value: any JSON}, default `[]` | Cases: One port per case, matched in order. Anything that matches no case leaves by the else port. |
| `compare` | "exact" or "ignoreCase", default `"exact"` | Compare text: Whether "Paid" and "paid" count as the same text. Numbers are not affected. |

Output: depends on the data.

```json
{"type":"core.switch","version":2,"label":"Switch","config":{"cases":[],"compare":"exact","expression":"<value to look at>"}}
```

### Older version `core.switch@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `expression` | text, required | Value to look at: Compared to each case's value exactly — a number never matches text. |
| `cases` | list of {id: text, label?: text, value: any JSON}, default `[]` | Cases: One port per case, matched in order. Anything that matches no case leaves by the else port. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"cases":[],"expression":"<value to look at>"}
{"cases":[],"compare":"exact","expression":"<value to look at>"}
```

## `core.tryCatch@1` — Try/Catch

Runs the steps you connect to try; if one fails, the flow goes on through catch instead of failing.

Ports: `try`, `success`, `catch` · changes nothing outside the run

Config: `{}` (no fields).

Output: `error` object (port `catch`) — Error · `error.code` string (port `catch`) — Error code · `error.message` string (port `catch`) — Error message · `node` object (port `catch`) — Failed step · `node.label` string (port `catch`) — Failed step name.

```json
{"type":"core.tryCatch","version":1,"label":"Try/Catch","config":{}}
```

## `core.waitFor@1` — Wait for signal

Holds the flow until a person lets it continue, or until the time you allow runs out.

Ports: `resumed`, `timeout` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `deadline.amount` | number, default `7` | Wait at most: A number, or a {{ }} expression that resolves to one. |
| `deadline.unit` | "minutes" or "hours" or "days", default `"days"` | Unit: From 1 minute to 31 days. A waiting run costs nothing while it waits and keeps its own plan; a connection or secret deleted meanwhile fails the next node cleanly. |
| `onTimeout` | "continue" or "fail", default `"continue"` | If nobody answers in time: Every wait ends: either somebody resumes the run, or the deadline does. |
| `resumeUrlEnabled` | true/false, default `false` | Let a URL resume this wait: Lets an outside system end this wait by posting to a URL — put {{ execution.resumeUrl }} in a node BEFORE this one and send it where it is needed. The call resumes the run on the resumed port and its JSON body becomes the payload. Anyone holding the URL can resume the run, so keep it inside your own systems. One wait per flow may have it, and never a wait inside a loop. |

Output: `resumedBy` string (port `resumed`) — Resumed by · `payload` any (port `resumed`) — Payload · `waitedMs` number (port `resumed`) — Waited (ms) · `resumedBy` string (port `timeout`) — Resumed by · `payload` any (port `timeout`) — Payload (empty after a timeout) · `waitedMs` number (port `timeout`) — Waited (ms).

```json
{"type":"core.waitFor","version":1,"label":"Wait for signal","config":{"deadline":{"amount":7,"unit":"days"},"onTimeout":"continue","resumeUrlEnabled":false}}
```


---

## File: references/nodes/crypto.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# crypto nodes (`crypto.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `crypto.hash@1` — Security — Hash

Makes a one-way fingerprint of text, to compare or check it later.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `text` | any JSON, optional | Text: Hashed as UTF-8 bytes. — its `{{ }}` must give a string |
| `algorithm` | "sha256" or "sha512" or "md5", default `"sha256"` | Algorithm: SHA-256 is the default; MD5 is for checksums of older systems. |
| `format` | "hex" or "base64", default `"hex"` | Output |

Output: `digest` string — Digest.

```json
{"type":"crypto.hash","version":1,"label":"Hash","config":{"algorithm":"sha256","format":"hex","text":"<text>"}}
```

## `crypto.hmac@1` — Security — HMAC

Signs text with a secret both sides know, so the other side can check it.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `text` | any JSON, optional | Text: Signed as UTF-8 bytes — the exact text the peer signs, character for character. — its `{{ }}` must give a string |
| `algorithm` | "sha256" or "sha512" or "md5", default `"sha256"` | Algorithm |
| `format` | "hex" or "base64", default `"hex"` | Output |
| `auth.secret` | text, required | Secret: Never printed, never logged — the signature is all that leaves. — write `{{ secret.NAME }}`, never the value |

Output: `signature` string — Signature.

```json
{"type":"crypto.hmac","version":1,"label":"HMAC","config":{"algorithm":"sha256","format":"hex","auth":{"secret":"<secret>"},"text":"<text>"}}
```

## `crypto.jwtSign@1` — Security — Sign JWT

Creates a login token (JWT) from the details you list, signed with a secret.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `claims` | object (any keys), default `{}` | Claims: The token's payload — sub, iss, role… A value can come from an earlier step; issued-at and expiry are added here. |
| `expiresIn` | text, default `"1h"` | Valid for: A duration like 15m, 1h or 7d — measured from signing time. |
| `algorithm` | "HS256" or "HS384" or "HS512", default `"HS256"` | Algorithm |
| `auth.secret` | text, required | Secret: Never printed, never logged — the token is all that leaves. — write `{{ secret.NAME }}`, never the value |

Output: `token` string — Token · `expiresAt` string — Expires at.

```json
{"type":"crypto.jwtSign","version":1,"label":"Sign JWT","config":{"claims":{},"expiresIn":"1h","algorithm":"HS256","auth":{"secret":"<secret>"}}}
```

## `crypto.jwtVerify@1` — Security — Verify JWT

Checks a token (JWT) is genuine and not expired, and hands back what is inside.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `jwt` | any JSON, optional | Token: The whole token — header.payload.signature, pasteable from a header or a webhook body. — its `{{ }}` must give a string |
| `algorithm` | "HS256" or "HS384" or "HS512", default `"HS256"` | Algorithm: The algorithm the issuer signs with. A token that names another one is refused. |
| `auth.secret` | text, required | Secret: Never printed, never logged — the token is never echoed either. — write `{{ secret.NAME }}`, never the value |

Output: depends on the data.

```json
{"type":"crypto.jwtVerify","version":1,"label":"Verify JWT","config":{"algorithm":"HS256","auth":{"secret":"<secret>"},"jwt":"<token>"}}
```


---

## File: references/nodes/csv.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# csv nodes (`csv.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `csv.build@1` — CSV — Build

Makes CSV text out of rows, ready to save as a file or attach to mail.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `rows` | any JSON, optional | Rows: A list of rows — CSV — Parse, List — Filter or Table — Select hands one over. — its `{{ }}` must give an array |
| `headers` | true/false, default `true` | Rows are objects: On: every row is an object and its keys become the columns (a header row is written). Off: every row is a list of values. |
| `delimiter` | "," or ";" or "\t" or "\|", default `","` | Delimiter |

Output: `text` string — CSV text · `rowCount` number — Row count.

```json
{"type":"csv.build","version":1,"label":"Build","config":{"headers":true,"delimiter":",","rows":"<rows>"}}
```

## `csv.parse@1` — CSV — Parse

Turns CSV text — a spreadsheet export — into rows the flow can read.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `text` | any JSON, optional | CSV text: The text of the file — File — Read hands it over with this name. — its `{{ }}` must give a string |
| `delimiter` | "auto" or "," or ";" or "\t" or "\|", default `"auto"` | Delimiter |
| `headers` | true/false, default `true` | First row is a header: On: every row is an object keyed by the header names. Off: every row is a list of values. |
| `encoding` | "utf-8" or "latin1", default `"utf-8"` | Encoding: UTF-8 drops a byte-order mark. Latin-1 reads the text as bytes, for older exports. |
| `convertTypes` | true/false, default `false` | Convert types: Numbers and true/false become real values when they round-trip exactly. Leading zeros and long numbers always stay text. |

Output: `rows` array — Rows · `headers` array — Column names · `rowCount` number — Row count · `delimiter` string — Delimiter used.

```json
{"type":"csv.parse","version":1,"label":"Parse","config":{"delimiter":"auto","headers":true,"encoding":"utf-8","convertTypes":false,"text":"<csv text>"}}
```


---

## File: references/nodes/docusign.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# DocuSign nodes (`docusign.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `docusign.correctRecipient@1` — DocuSign — Correct a recipient

Fixes a signer's name or e-mail on a DocuSign envelope already out.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `envelopeId` | text, required | Envelope id |
| `recipientId` | text, required | Recipient id: The recipient's id inside the envelope — the List recipients node returns it. |
| `name` | text, default `""` | New name |
| `email` | text, default `""` | New e-mail |

Output: `recipients` object — Recipients.

```json
{"type":"docusign.correctRecipient","version":1,"label":"Correct a recipient","config":{"name":"","email":"","connection":"<connection id>","envelopeId":"<envelope id>","recipientId":"<recipient id>"}}
```

## `docusign.createSigningLink@1` — DocuSign — Create signing link

Makes a one-time DocuSign signing URL for a recipient in your own app.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `envelopeId` | text, required | Envelope id |
| `recipientName` | text, required | Signer name |
| `recipientEmail` | text, required | Signer e-mail |
| `clientUserId` | text, required | Embedded user id: The same embedded user id you gave the signer when you sent the envelope. |
| `returnUrl` | text, required | Return to: Where DocuSign sends the signer when they are done — a page in your own app. |
| `authenticationMethod` | "none" or "email" or "phone", default `"none"` | Authentication |

Output: `url` string — Signing URL (single use).

```json
{"type":"docusign.createSigningLink","version":1,"label":"Create signing link","config":{"authenticationMethod":"none","connection":"<connection id>","envelopeId":"<envelope id>","recipientName":"<signer name>","recipientEmail":"<signer e-mail>","clientUserId":"<embedded user id>","returnUrl":"<return to>"}}
```

## `docusign.downloadDocument@1` — DocuSign — Download document

Downloads a signed DocuSign document, or all of them as one PDF.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `envelopeId` | text, required | Envelope id |
| `documentSet` | "combined" or "archive" or "certificate" or "portfolio" or "specific", default `"combined"` | Which file: DocuSign throttles downloads per document: 10 in flight per hour for one document, 3 for the combined PDF. A loop over many envelopes hits it. |
| `documentId` | text, default `""` | Document id: The numeric document id — the List documents node returns it. — used when `documentSet` is "specific" |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"docusign.downloadDocument","version":1,"label":"Download document","config":{"documentSet":"combined","documentId":"","connection":"<connection id>","envelopeId":"<envelope id>"}}
```

## `docusign.getEnvelope@1` — DocuSign — Get envelope

Reads a DocuSign envelope's status and its recipients' progress.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `envelopeId` | text, required | Envelope id |
| `include` | text, default `"recipients"` | Include: Comma-separated: recipients, documents, tabs. DocuSign allows ONE status check per envelope per 15 minutes — put this behind a schedule of 20 minutes or more, or use a DocuSign Connect webhook instead. |

Output: `envelopeId` string — Envelope id · `status` string — Status · `statusChangedDateTime` string — Status changed at · `sentDateTime` string — Sent at · `completedDateTime` string — Completed at · `recipients` object — Recipients · `envelope` object — Envelope.

```json
{"type":"docusign.getEnvelope","version":1,"label":"Get envelope","config":{"include":"recipients","connection":"<connection id>","envelopeId":"<envelope id>"}}
```

## `docusign.listDocuments@1` — DocuSign — List documents

Lists the documents inside a DocuSign envelope.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `envelopeId` | text, required | Envelope id |

Output: `documents` array — Documents · `documentCount` number — Documents in this envelope.

```json
{"type":"docusign.listDocuments","version":1,"label":"List documents","config":{"connection":"<connection id>","envelopeId":"<envelope id>"}}
```

## `docusign.listEnvelopes@2` — DocuSign — List envelopes

Lists DocuSign envelopes changed since a date, filtered by status.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `fromDate` | text, required | Changed since: DocuSign requires it — ISO 8601, e.g. 2026-09-01T00:00:00Z. |
| `status` | text, default `""` | Status |
| `searchText` | text, default `""` | Search: Free text over the envelope's subject and recipients. |
| `count` | number, default `50` | Envelopes per call: 1 to 100. |
| `startPosition` | number, default `0` | Skip envelopes |
| `returnAll` | true/false, default `false` | Fetch every page: Pages until DocuSign runs out, and refuses past 2,000 envelopes rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `envelopes` array — Envelopes · `envelopeCount` number — Envelopes in this answer · `totalSetSize` number — How many DocuSign counted · `hasMore` boolean — More envelopes are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"docusign.listEnvelopes","version":2,"label":"List envelopes","config":{"status":"","searchText":"","count":50,"startPosition":0,"returnAll":false,"cursor":"","connection":"<connection id>","fromDate":"<changed since>"}}
```

### Older version `docusign.listEnvelopes@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `fromDate` | text, required | Changed since: DocuSign requires it — ISO 8601, e.g. 2026-09-01T00:00:00Z. |
| `status` | text, default `""` | Status |
| `searchText` | text, default `""` | Search: Free text over the envelope's subject and recipients. |
| `count` | number, default `50` | Envelopes per call: 1 to 100. |
| `startPosition` | number, default `0` | Skip envelopes |
| `returnAll` | true/false, default `false` | Fetch every page: Pages until DocuSign runs out, and refuses past 2,000 envelopes rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"status":"","searchText":"","count":50,"startPosition":0,"returnAll":false,"connection":"<connection id>","fromDate":"<changed since>"}
{"status":"","searchText":"","count":50,"startPosition":0,"returnAll":false,"cursor":"","connection":"<connection id>","fromDate":"<changed since>"}
```

## `docusign.listRecipients@1` — DocuSign — List recipients

Reads who a DocuSign envelope went to and who has signed.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `envelopeId` | text, required | Envelope id |

Output: `signers` array — Signers · `carbonCopies` array — Carbon copies · `recipientCount` number — Recipients in this answer.

```json
{"type":"docusign.listRecipients","version":1,"label":"List recipients","config":{"connection":"<connection id>","envelopeId":"<envelope id>"}}
```

## `docusign.listTemplates@1` — DocuSign — List templates

Lists the DocuSign templates you can send from.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `searchText` | text, default `""` | Search |
| `count` | number, default `50` | Templates per call: 1 to 100. |
| `startPosition` | number, default `0` | Skip templates |
| `returnAll` | true/false, default `false` | Fetch every page: Pages until DocuSign runs out, and refuses past 2,000 templates — DocuSign's own cap on this call. |

Output: `templates` array — Templates · `templateCount` number — Templates in this answer.

```json
{"type":"docusign.listTemplates","version":1,"label":"List templates","config":{"searchText":"","count":50,"startPosition":0,"returnAll":false,"connection":"<connection id>"}}
```

## `docusign.sendDocuments@1` — DocuSign — Send documents

Sends one or more files out for signature through DocuSign.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `documents` | list of {contentBase64?: text, name?: text, fileExtension?: text}, default `[]` | Documents: One row per file: paste {{ file("contract.pdf").base64 }} as the content, name it, and give its extension. At most 32 MB per file and 200 MB per envelope — DocuSign refuses more. |
| `signers` | list of {name?: text, email?: text, routingOrder?: text, clientUserId?: text, anchorString?: text}, default `[]` | Signers: One row per signer. An anchor string like /sig1/ places the signature where the document already says so. |
| `carbonCopies` | list of {name?: text, email?: text, routingOrder?: text}, default `[]` | Carbon copies |
| `emailSubject` | text, required | E-mail subject |
| `emailBlurb` | text, default `""` | E-mail message |
| `sendNow` | true/false, default `true` | Send now: Off saves a draft. |

Output: `envelopeId` string — Envelope id · `status` string — Status · `uri` string — Envelope URI.

```json
{"type":"docusign.sendDocuments","version":1,"label":"Send documents","config":{"documents":[],"signers":[],"carbonCopies":[],"emailBlurb":"","sendNow":true,"connection":"<connection id>","emailSubject":"<e-mail subject>"}}
```

## `docusign.sendDraftEnvelope@1` — DocuSign — Send a draft

Sends a DocuSign envelope that was saved as a draft.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `envelopeId` | text, required | Envelope id |

Output: `envelopeId` string — Envelope id · `status` string — Status.

```json
{"type":"docusign.sendDraftEnvelope","version":1,"label":"Send a draft","config":{"connection":"<connection id>","envelopeId":"<envelope id>"}}
```

## `docusign.sendFromTemplate@1` — DocuSign — Send from template

Creates an envelope from a DocuSign template and sends it for signature.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `templateId` | text, required | Template ID: Pick it from DocuSign → Templates → the template's ID. |
| `roles` | list of {roleName?: text, name?: text, email?: text, routingOrder?: text, clientUserId?: text}, default `[]` | Roles: One row per template role. The role name must match the template's own; the embedded user id is only for signing inside your own app. |
| `emailSubject` | text, default `""` | E-mail subject |
| `emailBlurb` | text, default `""` | E-mail message |
| `sendNow` | true/false, default `true` | Send now: Off saves a draft in DocuSign instead of sending it. |

Output: `envelopeId` string — Envelope id · `status` string — Status · `statusDateTime` string — Status changed at · `uri` string — Envelope URI.

```json
{"type":"docusign.sendFromTemplate","version":1,"label":"Send from template","config":{"roles":[],"emailSubject":"","emailBlurb":"","sendNow":true,"connection":"<connection id>","templateId":"<template id>"}}
```

## `docusign.voidEnvelope@1` — DocuSign — Void envelope

Cancels a DocuSign envelope that is out for signature, with a reason.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `docusign`) |
| `envelopeId` | text, required | Envelope id |
| `reason` | text, required | Reason: Required — DocuSign puts it in the mail every signer receives. |

Output: `envelopeId` string — Envelope id · `status` string — Status.

```json
{"type":"docusign.voidEnvelope","version":1,"label":"Void envelope","config":{"connection":"<connection id>","envelopeId":"<envelope id>","reason":"<reason>"}}
```


---

## File: references/nodes/exact.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Exact Online nodes (`exact.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `exact.create@1` — Exact Online — Create

Creates a record in Exact Online.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `exact-online`) |
| `division` | text, default `""` | Division: Leave empty to use the administration the connection signed in to. |
| `resource` | text, required | Resource |
| `data` | any JSON, optional | Fields: The record's fields as JSON — or one {{ }} expression that produces the object. |

Output: `record` object — Created record · `id` string — The new record's ID.

```json
{"type":"exact.create","version":1,"label":"Create","config":{"division":"","connection":"<connection id>","resource":"<resource>"}}
```

## `exact.createInvoice@1` — Exact Online — Create invoice

Creates a sales invoice in Exact Online.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `exact-online`) |
| `division` | text, default `""` | Division: Leave empty to use the administration the connection signed in to. |
| `debtor` | text or number, required | Debtor: The customer's account in Exact Online, by its ID. |
| `invoiceNumber` | text or number, default `""` | Invoice number: Leave empty and Exact Online numbers the invoice itself. |
| `lines` | any JSON, optional | Lines: A list of the invoice's lines, from an earlier step. |
| `journal` | text or number, required | Journal: The sales journal's code, as Exact Online has it (for example 70). |

Output: `record` object — Created invoice · `id` string — The new invoice's ID.

```json
{"type":"exact.createInvoice","version":1,"label":"Create invoice","config":{"division":"","invoiceNumber":"","connection":"<connection id>","debtor":"<debtor>","journal":"<journal>"}}
```

## `exact.delete@1` — Exact Online — Delete

Deletes a record from Exact Online.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `exact-online`) |
| `division` | text, default `""` | Division: Leave empty to use the administration the connection signed in to. |
| `resource` | text, required | Resource |
| `recordId` | text, required | Record ID |

Output: `deleted` boolean — Deleted.

```json
{"type":"exact.delete","version":1,"label":"Delete","config":{"division":"","connection":"<connection id>","resource":"<resource>","recordId":"<record id>"}}
```

## `exact.get@2` — Exact Online — Get

Fetches records out of Exact Online, page by page or all at once.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `exact-online`) |
| `division` | text, default `""` | Division: Leave empty to use the administration the connection signed in to. |
| `resource` | text, required | Resource: The Exact Online resource path after the division — its documentation calls this the endpoint. |
| `select` | text, default `""` | Fields: The fields to return, comma-separated. Fewer fields is faster and counts the same against your limit. |
| `filter` | text, default `""` | Filter: An OData filter, spelled as Exact Online spells it. |
| `orderBy` | text, default `""` | Order by |
| `top` | number, default `60` | Records per call: Records per call. Most Exact Online resources page at 60; the sync and bulk resources page at 1,000. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Exact Online's own next-page link until it runs out, and refuses past 25,000 records rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `records` array — Records · `recordCount` number — Records in this answer · `hasMore` boolean — More records are waiting behind this page · `nextLink` string — Exact Online's own link to the next page · `division` string — The division these records came from · `nextCursor` string — Cursor for the next page.

```json
{"type":"exact.get","version":2,"label":"Get","config":{"division":"","select":"","filter":"","orderBy":"","top":60,"returnAll":false,"cursor":"","connection":"<connection id>","resource":"<resource>"}}
```

### Older version `exact.get@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `exact-online`) |
| `division` | text, default `""` | Division: Leave empty to use the administration the connection signed in to. |
| `resource` | text, required | Resource: The Exact Online resource path after the division — its documentation calls this the endpoint. |
| `select` | text, default `""` | Fields: The fields to return, comma-separated. Fewer fields is faster and counts the same against your limit. |
| `filter` | text, default `""` | Filter: An OData filter, spelled as Exact Online spells it. |
| `orderBy` | text, default `""` | Order by |
| `top` | number, default `60` | Records per call: Records per call. Most Exact Online resources page at 60; the sync and bulk resources page at 1,000. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Exact Online's own next-page link until it runs out, and refuses past 25,000 records rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"division":"","select":"","filter":"","orderBy":"","top":60,"returnAll":false,"connection":"<connection id>","resource":"<resource>"}
{"division":"","select":"","filter":"","orderBy":"","top":60,"returnAll":false,"cursor":"","connection":"<connection id>","resource":"<resource>"}
```

## `exact.listDivisions@1` — Exact Online — List administrations

Lists the Exact Online administrations this connection may use.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `exact-online`) |

Output: `currentDivision` string — The administration this connection signed in to · `divisions` array — Administrations this user may use · `divisionCount` number — How many.

```json
{"type":"exact.listDivisions","version":1,"label":"List administrations","config":{"connection":"<connection id>"}}
```

## `exact.update@1` — Exact Online — Update

Changes an existing Exact Online record.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `exact-online`) |
| `division` | text, default `""` | Division: Leave empty to use the administration the connection signed in to. |
| `resource` | text, required | Resource |
| `recordId` | text, required | Record ID: The record's GUID — a Get node hands you records[].ID. |
| `data` | any JSON, optional | Fields: The fields to change as JSON, sent as a change to one record. A failed update is not retried on its own, because a body that adds a line to a list would add it twice. |

Output: `updated` boolean — Updated · `record` any — Record (Exact answers 204 with no body on success, so this is null).

```json
{"type":"exact.update","version":1,"label":"Update","config":{"division":"","connection":"<connection id>","resource":"<resource>","recordId":"<record id>"}}
```


---

## File: references/nodes/file.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# file nodes (`file.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `file.delete@1` — File — Delete

Deletes a file and every version of it — there is no undo.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `name` | text, required | File name: Deletes the file and every version of it; an archived file is deleted too. A name that does not exist is not an error — the node answers deleted: false. — a file name, listed in `requires.files` |

Output: `deleted` boolean — Deleted (false when the file was already gone) · `name` string — File name.

```json
{"type":"file.delete","version":1,"label":"Delete","config":{"name":"<file name>"}}
```

## `file.list@1` — File — List

Lists the files in storage, or only those whose name begins with what you type.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `prefix` | text, default `""` | Name prefix: Byte-exact and case-sensitive. Lists the environment this run is in, 1,000 files per page. — the start of file names |
| `includeArchived` | true/false, default `false` | Include archived files: Archived files are listed but cannot be read — leave this off before a loop. |
| `ifMoreFiles` | "fail" or "first", default `"fail"` | If more files: Return the first page with a nextCursor to list a folder of any size in a loop. |
| `cursor` | text, default `""` | Cursor: Continue a previous list from its nextCursor. |

Output: `files` array — Files · `count` number — Number of files · `nextCursor` string — Cursor for the next page.

```json
{"type":"file.list","version":1,"label":"List","config":{"prefix":"","includeArchived":false,"ifMoreFiles":"fail","cursor":""}}
```

## `file.read@1` — File — Read

Reads a file from storage so the flow can use what is in it.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `name` | text, required | File name: A file's name is the whole key — exports/report.csv is a name, not a folder. — a file name, listed in `requires.files` |
| `encoding` | "text" or "base64", default `"text"` | Encoding: One read carries up to 5 MB; a larger file fails unless you read it in parts. |
| `offset` | number, default `0` | Offset: The byte to start at — 0, or the nextOffset of the previous part. |
| `length` | number, optional | Length: Read at most this many bytes (up to 5 MB) — a part, never a failure. A text part ends on a whole character. |
| `allowPartial` | true/false, default `false` | Allow a partial read: Past 5 MB, return the first part with truncated and nextOffset instead of failing. |

Output: `name` string — File name · `contentType` string — Content type · `size` number — Size (bytes) · `binary` boolean — Binary (text reads answer metadata only) · `truncated` boolean — A part — the file goes on past it · `nextOffset` number — Offset of the next part (only on a part) · `text` string — Text (text encoding only) · `base64` string — Base64 (base64 encoding only).

```json
{"type":"file.read","version":1,"label":"Read","config":{"encoding":"text","offset":0,"allowPartial":false,"name":"<file name>"}}
```

## `file.write@2` — Save a file

Saves data from the flow as a file in storage, or as a new version of one.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `name` | text, required | File name: A file's name is the whole key — exports/report.csv is a name, not a folder. A name that already exists gets a new version — the last 10 are kept. |
| `content` | text, default `""` | Content: Text or base64 per the encoding; expressions welcome. Empty writes a 0-byte file. |
| `encoding` | "text" or "base64", default `"text"` | Encoding: Pick Base64 for bytes — an image, a PDF, anything HTTP Request returned as Base64. Base64 content must be valid base64, or the write fails. One version is at most 100 MB. |
| `contentType` | text, optional | Content type: Only for a NEW file — the type is fixed across versions after that. Left empty it is detected from the content, then from the name. |
| `folderId` | text, optional | Folder: The Storage folder a new file goes in. A new version stays wherever its file is, and a folder deleted since means the file lands unfiled. — a Storage folder id, or empty for unfiled |

Output: `name` string — File name · `fileId` string — File id · `contentType` string — Content type · `size` number — Size (bytes) · `sha256` string — SHA-256 of the content · `kind` string — Result — created, versioned or noop · `versionNumber` number — Version number (absent on a no-op write).

```json
{"type":"file.write","version":2,"label":"Save a file","config":{"content":"","encoding":"text","name":"<file name>"}}
```

### Older version `file.write@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `name` | text, required | File name: A file's name is the whole key — exports/report.csv is a name, not a folder. A name that already exists gets a new version — the last 10 are kept. |
| `content` | text, default `""` | Content: Text or base64 per the encoding; expressions welcome. Empty writes a 0-byte file. |
| `encoding` | "text" or "base64", default `"text"` | Encoding: Pick Base64 for bytes — an image, a PDF, anything HTTP Request returned as Base64. Base64 content must be valid base64, or the write fails. One version is at most 100 MB. |
| `contentType` | text, optional | Content type: Only for a NEW file — the type is fixed across versions after that. Left empty it is detected from the content, then from the name. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"content":"","encoding":"text","name":"<file name>"}
{"content":"","encoding":"text","name":"<file name>"}
```


---

## File: references/nodes/freshdesk.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Freshdesk nodes (`freshdesk.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `freshdesk.addNote@1` — Freshdesk — Add note

Adds a private or public note to a Freshdesk ticket.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `ticketId` | text, required | Ticket id |
| `bodyFormat` | "text" or "html", default `"text"` | Note format: Freshdesk renders a note as HTML. Plain text escapes what you write and turns your line breaks into HTML ones. |
| `body` | text, required | Note |
| `private` | true/false, default `true` | Private: On, only agents see it. Off, the requester sees it in the portal — Freshdesk still does not e-mail anyone unless you fill Notify. |
| `notifyEmails` | text, default `""` | Notify: Freshdesk e-mails these addresses about the note. |
| `userId` | text, default `""` | Add as agent id |
| `incoming` | true/false, default `false` | Mark as coming from the requester: Records the note as if the requester wrote it, rather than an agent. |
| `attachments` | object of text, default `{}` | Attachments: File name → its base64, for example {{ file("report.pdf").base64 }}. All attachments together may not exceed 20 MB. |

Output: `conversationId` number — Note id · `conversation` object — Note.

```json
{"type":"freshdesk.addNote","version":1,"label":"Add note","config":{"bodyFormat":"text","private":true,"notifyEmails":"","userId":"","incoming":false,"attachments":{},"connection":"<connection id>","ticketId":"<ticket id>","body":"<note>"}}
```

## `freshdesk.addTimeEntry@1` — Freshdesk — Log time on a ticket

Adds a time entry to a Freshdesk ticket.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `ticketId` | text, required | Ticket id |
| `timeSpent` | text, default `""` | Time spent: Hours and minutes as hh:mm. Leave it empty only when you start a running timer instead. |
| `billable` | true/false, default `true` | Billable |
| `note` | text, default `""` | Note |
| `agentId` | text, default `""` | Log for agent id: Empty logs it for the agent the API key belongs to. — a value from the vendor's own list |
| `executedAt` | text, default `""` | Work done at |
| `startTime` | text, default `""` | Started at |
| `timerRunning` | true/false, default `false` | Start a running timer: Freshdesk keeps counting until somebody stops the timer in Freshdesk itself. |

Output: `timeEntryId` number — Time entry id · `timeEntry` object — Time entry.

```json
{"type":"freshdesk.addTimeEntry","version":1,"label":"Log time on a ticket","config":{"timeSpent":"","billable":true,"note":"","agentId":"","executedAt":"","startTime":"","timerRunning":false,"connection":"<connection id>","ticketId":"<ticket id>"}}
```

## `freshdesk.createContact@1` — Freshdesk — Create contact

Creates a Freshdesk contact.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `name` | text, default `""` | Name: Freshdesk requires a name whenever there is no e-mail address. |
| `email` | text, default `""` | E-mail |
| `phone` | text, default `""` | Phone |
| `mobile` | text, default `""` | Mobile |
| `twitterId` | text, default `""` | Twitter handle |
| `uniqueExternalId` | text, default `""` | External id: Your own id for this person, so a later run can find them again. |
| `otherEmails` | text, default `""` | Other e-mail addresses |
| `companyId` | text, default `""` | Company id |
| `viewAllTickets` | true/false, default `false` | May see every ticket of the company |
| `address` | text, default `""` | Address |
| `jobTitle` | text, default `""` | Job title |
| `language` | text, default `""` | Language |
| `timeZone` | text, default `""` | Time zone |
| `description` | text, default `""` | Notes |
| `tags` | text, default `""` | Tags: Comma-separated. |
| `customFields` | object of text, default `{}` | Custom fields: Keyed by the field's own name in Freshdesk, usually prefixed cf_. |

Output: `contactId` number — Contact id · `contact` object — Contact.

```json
{"type":"freshdesk.createContact","version":1,"label":"Create contact","config":{"name":"","email":"","phone":"","mobile":"","twitterId":"","uniqueExternalId":"","otherEmails":"","companyId":"","viewAllTickets":false,"address":"","jobTitle":"","language":"","timeZone":"","description":"","tags":"","customFields":{},"connection":"<connection id>"}}
```

## `freshdesk.createTicket@1` — Freshdesk — Create ticket

Creates a Freshdesk ticket for a requester you name.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `requesterMode` | "email" or "requesterId" or "phone" or "uniqueExternalId", default `"email"` | Identify the requester by: Freshdesk needs exactly one of these. A new e-mail address creates the contact; a phone number needs a name beside it. |
| `requester` | text, required | Requester |
| `name` | text, default `""` | Requester name: Freshdesk refuses a ticket identified by phone number without a name. — used when `requesterMode` is "phone" |
| `subject` | text, default `""` | Subject |
| `descriptionFormat` | "text" or "html", default `"text"` | Description format: Freshdesk renders a description as HTML. Plain text escapes what you write and turns your line breaks into HTML ones; HTML sends it exactly as typed. |
| `description` | text, required | Description: The first message of the ticket, as the requester will see it. |
| `status` | "open" or "pending" or "resolved" or "closed", default `"open"` | Status: Freshdesk stores a status as a number; the number is in brackets so an export reads the same as this list. |
| `priority` | "low" or "medium" or "high" or "urgent", default `"low"` | Priority |
| `source` | "email" or "portal" or "phone" or "chat" or "feedback_widget" or "outbound_email", default `"portal"` | Source |
| `type` | text, default `""` | Type: The ticket type as your Freshdesk admin named it, for example Incident. |
| `responderId` | text, default `""` | Assign to agent id: The List agents node gives you the ids. — a value from the vendor's own list |
| `groupId` | text, default `""` | Group id: The List groups node gives you the ids. — a value from the vendor's own list |
| `companyId` | text, default `""` | Company id |
| `productId` | text, default `""` | Product id |
| `emailConfigId` | text, default `""` | E-mail config id: Which of your support addresses the ticket belongs to. |
| `parentId` | text, default `""` | Parent ticket id |
| `ccEmails` | text, default `""` | Cc |
| `tags` | text, default `""` | Tags: Comma-separated. |
| `customFields` | object of text, default `{}` | Custom fields: Freshdesk keys a custom field by its name, usually prefixed cf_ — the List ticket fields node gives you the names and the allowed choices. |
| `dueBy` | text, default `""` | Due by: Freshdesk refuses one of the two due dates without the other. |
| `frDueBy` | text, default `""` | First response due by |
| `attachments` | object of text, default `{}` | Attachments: File name → its base64, for example {{ file("report.pdf").base64 }}. All attachments together may not exceed 20 MB. |

Output: `ticketId` number — Ticket id · `ticket` object — Ticket · `ticketUrl` string — Link to the ticket.

```json
{"type":"freshdesk.createTicket","version":1,"label":"Create ticket","config":{"requesterMode":"email","name":"","subject":"","descriptionFormat":"text","status":"open","priority":"low","source":"portal","type":"","responderId":"","groupId":"","companyId":"","productId":"","emailConfigId":"","parentId":"","ccEmails":"","tags":"","customFields":{},"dueBy":"","frDueBy":"","attachments":{},"connection":"<connection id>","requester":"<requester>","description":"<description>"}}
```

## `freshdesk.findCompanies@1` — Freshdesk — Find companies

Lists or searches Freshdesk companies.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `mode` | "list" or "search" or "autocomplete", default `"list"` | How to find them |
| `query` | text, default `""` | Query: Freshdesk searches companies on name, domain, created_at, updated_at and any custom field by name. A query may be 512 characters and answers at most 10 pages of 30. — used when `mode` is "search" |
| `name` | text, default `""` | Name starts with — used when `mode` is "autocomplete" |
| `limit` | number, default `100` | Companies per call: 1 to 100. A filter query always answers pages of 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Refuses past 5,000 companies rather than answering a short list. Name completion answers one page and never pages. |
| `maxPages` | number, default `10` | Pages at most: 1 to 10. — used when `mode` is "search" |

Output: `companies` array — Companies · `companyCount` number — Companies in this answer · `hasMore` boolean — More companies are waiting behind this page · `nextUrl` string — Freshdesk's link to the next page.

```json
{"type":"freshdesk.findCompanies","version":1,"label":"Find companies","config":{"mode":"list","query":"","name":"","limit":100,"returnAll":false,"maxPages":10,"connection":"<connection id>"}}
```

## `freshdesk.findContacts@1` — Freshdesk — Find contacts

Lists or searches Freshdesk contacts.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `mode` | "list" or "search", default `"list"` | How to find them |
| `email` | text, default `""` | E-mail — used when `mode` is "list" |
| `mobile` | text, default `""` | Mobile — used when `mode` is "list" |
| `phone` | text, default `""` | Phone — used when `mode` is "list" |
| `companyId` | text, default `""` | Company id — used when `mode` is "list" |
| `state` | "" or "blocked" or "deleted" or "unverified" or "verified", default `""` | State — used when `mode` is "list" |
| `updatedSince` | text, default `""` | Updated since — used when `mode` is "list" |
| `query` | text, default `""` | Query: Freshdesk searches contacts on name, email, phone, mobile, company_id, state, created_at, updated_at and any custom field by name. A query may be 512 characters and answers at most 10 pages of 30. — used when `mode` is "search" |
| `limit` | number, default `100` | Contacts per call: 1 to 100. A filter query always answers pages of 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Refuses past 5,000 contacts rather than answering a short list. A query stops at 10 pages of 30 whatever you set. |
| `maxPages` | number, default `10` | Pages at most: 1 to 10. — used when `mode` is "search" |

Output: `contacts` array — Contacts · `contactCount` number — Contacts in this answer · `total` number — Matches Freshdesk counted (a query only) · `hasMore` boolean — More contacts are waiting behind this page · `nextUrl` string — Freshdesk's link to the next page.

```json
{"type":"freshdesk.findContacts","version":1,"label":"Find contacts","config":{"mode":"list","email":"","mobile":"","phone":"","companyId":"","state":"","updatedSince":"","query":"","limit":100,"returnAll":false,"maxPages":10,"connection":"<connection id>"}}
```

## `freshdesk.getContact@1` — Freshdesk — Get contact

Reads one Freshdesk contact by id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `contactId` | text, required | Contact id |

Output: `contact` object — Contact · `contactId` number — Contact id.

```json
{"type":"freshdesk.getContact","version":1,"label":"Get contact","config":{"connection":"<connection id>","contactId":"<contact id>"}}
```

## `freshdesk.getTicket@1` — Freshdesk — Get ticket

Reads one Freshdesk ticket, with the extras you ask for.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `ticketId` | text, required | Ticket id |
| `include` | text, default `""` | Extras: Comma-separated. Each extra costs an extra API call credit, and conversations returns at most the first 10 and costs two. |

Output: `ticket` object — Ticket · `ticketId` number — Ticket id · `ticketUrl` string — Link to the ticket · `conversations` array — Replies and notes (when asked for) · `requester` object — Requester (when asked for) · `company` object — Company (when asked for) · `stats` object — Timestamps (when asked for).

```json
{"type":"freshdesk.getTicket","version":1,"label":"Get ticket","config":{"include":"","connection":"<connection id>","ticketId":"<ticket id>"}}
```

## `freshdesk.listAgents@1` — Freshdesk — List agents

Lists Freshdesk agents and their ids.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `email` | text, default `""` | E-mail |
| `phone` | text, default `""` | Phone |
| `mobile` | text, default `""` | Mobile |
| `state` | "" or "fulltime" or "occasional", default `""` | Kind |
| `limit` | number, default `100` | Agents per call: 1 to 100. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Freshdesk's own next-page link, and refuses past 5,000 agents. |

Output: `agents` array — Agents · `agentCount` number — Agents in this answer · `hasMore` boolean — More agents are waiting behind this page · `nextUrl` string — Freshdesk's link to the next page.

```json
{"type":"freshdesk.listAgents","version":1,"label":"List agents","config":{"email":"","phone":"","mobile":"","state":"","limit":100,"returnAll":true,"connection":"<connection id>"}}
```

## `freshdesk.listConversations@1` — Freshdesk — List conversations

Reads the replies and notes on a Freshdesk ticket.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `ticketId` | text, required | Ticket id |
| `limit` | number, default `100` | Messages per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Freshdesk's own next-page link until the thread ends. |

Output: `conversations` array — Replies and notes · `conversationCount` number — Messages in this answer · `hasMore` boolean — More messages are waiting behind this page · `nextUrl` string — Freshdesk's link to the next page.

```json
{"type":"freshdesk.listConversations","version":1,"label":"List conversations","config":{"limit":100,"returnAll":false,"connection":"<connection id>","ticketId":"<ticket id>"}}
```

## `freshdesk.listGroups@1` — Freshdesk — List groups

Lists Freshdesk groups and their ids.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `limit` | number, default `100` | Groups per call: 1 to 100. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Freshdesk's own next-page link, and refuses past 5,000 groups. |

Output: `groups` array — Groups · `groupCount` number — Groups in this answer · `hasMore` boolean — More groups are waiting behind this page · `nextUrl` string — Freshdesk's link to the next page.

```json
{"type":"freshdesk.listGroups","version":1,"label":"List groups","config":{"limit":100,"returnAll":true,"connection":"<connection id>"}}
```

## `freshdesk.listTicketFields@1` — Freshdesk — List ticket fields

Lists Freshdesk ticket fields, their names and their choices.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `type` | text, default `""` | Only fields of this kind: Leave empty for every field. |

Output: `fields` array — Ticket fields · `fieldCount` number — Fields in this answer · `customFieldNames` object — Custom field name → its kind · `choices` object — Field name → the values it allows.

```json
{"type":"freshdesk.listTicketFields","version":1,"label":"List ticket fields","config":{"type":"","connection":"<connection id>"}}
```

## `freshdesk.listTickets@1` — Freshdesk — List tickets

Lists Freshdesk tickets, newest first, with the filters Freshdesk offers.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `filter` | "" or "new_and_my_open" or "watching" or "spam" or "deleted", default `""` | Predefined filter |
| `requesterId` | text, default `""` | Requester id |
| `email` | text, default `""` | Requester e-mail |
| `uniqueExternalId` | text, default `""` | Requester external id |
| `companyId` | text, default `""` | Company id |
| `updatedSince` | text, default `""` | Updated since: Without this, Freshdesk only returns tickets created in the last 30 days. That is its single most surprising rule. |
| `orderBy` | "created_at" or "due_by" or "updated_at" or "status", default `"created_at"` | Order by |
| `orderType` | "desc" or "asc", default `"desc"` | Direction |
| `include` | text, default `""` | Extras: Comma-separated. Each extra costs an extra API call credit per ticket. |
| `limit` | number, default `100` | Tickets per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Freshdesk's own next-page link, and refuses past 5,000 tickets or 300 pages rather than answering a short list. |

Output: `tickets` array — Tickets · `ticketCount` number — Tickets in this answer · `hasMore` boolean — More tickets are waiting behind this page · `nextUrl` string — Freshdesk's link to the next page.

```json
{"type":"freshdesk.listTickets","version":1,"label":"List tickets","config":{"filter":"","requesterId":"","email":"","uniqueExternalId":"","companyId":"","updatedSince":"","orderBy":"created_at","orderType":"desc","include":"","limit":100,"returnAll":false,"connection":"<connection id>"}}
```

## `freshdesk.replyToTicket@1` — Freshdesk — Reply to ticket

Sends a reply to the requester of a Freshdesk ticket.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `ticketId` | text, required | Ticket id |
| `bodyFormat` | "text" or "html", default `"text"` | Message format: Freshdesk renders a reply as HTML. Plain text escapes what you write and turns your line breaks into HTML ones; HTML sends it exactly as typed. |
| `body` | text, required | Message: This is e-mailed to the requester. |
| `fromEmail` | text, default `""` | From address: One of your Freshdesk support addresses. Empty uses the ticket's own. |
| `userId` | text, default `""` | Send as agent id |
| `ccEmails` | text, default `""` | Cc |
| `bccEmails` | text, default `""` | Bcc |
| `attachments` | object of text, default `{}` | Attachments: File name → its base64, for example {{ file("report.pdf").base64 }}. All attachments together may not exceed 20 MB. |

Output: `conversationId` number — Reply id · `conversation` object — Reply.

```json
{"type":"freshdesk.replyToTicket","version":1,"label":"Reply to ticket","config":{"bodyFormat":"text","fromEmail":"","userId":"","ccEmails":"","bccEmails":"","attachments":{},"connection":"<connection id>","ticketId":"<ticket id>","body":"<message>"}}
```

## `freshdesk.searchTickets@2` — Freshdesk — Search tickets

Finds Freshdesk tickets with a filter query.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `query` | text, required | Query: Freshdesk searches on agent_id, group_id, priority, status, type, tag, due_by, fr_due_by, created_at, updated_at, closed_at and any custom field by name. It understands AND, OR, brackets, :, :> and :<, and the word null. A query may be 512 characters and answers at most 10 pages of 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Freshdesk stops at 10 pages of 30 results, whatever you set below. |
| `maxPages` | number, default `10` | Pages at most: 1 to 10. — used when `returnAll` is true |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `tickets` array — Tickets · `ticketCount` number — Tickets in this answer · `total` number — Matches Freshdesk counted · `hasMore` boolean — More tickets are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"freshdesk.searchTickets","version":2,"label":"Search tickets","config":{"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","query":"<query>"}}
```

### Older version `freshdesk.searchTickets@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `query` | text, required | Query: Freshdesk searches on agent_id, group_id, priority, status, type, tag, due_by, fr_due_by, created_at, updated_at, closed_at and any custom field by name. It understands AND, OR, brackets, :, :> and :<, and the word null. A query may be 512 characters and answers at most 10 pages of 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Freshdesk stops at 10 pages of 30 results, whatever you set below. |
| `maxPages` | number, default `10` | Pages at most: 1 to 10. — used when `returnAll` is true |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"returnAll":false,"maxPages":10,"connection":"<connection id>","query":"<query>"}
{"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","query":"<query>"}
```

## `freshdesk.updateContact@1` — Freshdesk — Update contact

Updates a Freshdesk contact by id.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `contactId` | text, required | Contact id |
| `name` | text, default `""` | Name |
| `email` | text, default `""` | E-mail |
| `phone` | text, default `""` | Phone |
| `mobile` | text, default `""` | Mobile |
| `uniqueExternalId` | text, default `""` | External id |
| `otherEmails` | text, default `""` | Other e-mail addresses: Freshdesk REPLACES the whole list with what you send. |
| `companyId` | text, default `""` | Company id |
| `viewAllTickets` | "" or "yes" or "no", default `""` | May see every ticket of the company |
| `address` | text, default `""` | Address |
| `jobTitle` | text, default `""` | Job title |
| `language` | text, default `""` | Language |
| `timeZone` | text, default `""` | Time zone |
| `description` | text, default `""` | Notes |
| `tags` | text, default `""` | Tags: Comma-separated. Freshdesk REPLACES the whole tag list with what you send. |
| `customFields` | object of text, default `{}` | Custom fields |

Output: `contactId` number — Contact id · `contact` object — Contact.

```json
{"type":"freshdesk.updateContact","version":1,"label":"Update contact","config":{"name":"","email":"","phone":"","mobile":"","uniqueExternalId":"","otherEmails":"","companyId":"","viewAllTickets":"","address":"","jobTitle":"","language":"","timeZone":"","description":"","tags":"","customFields":{},"connection":"<connection id>","contactId":"<contact id>"}}
```

## `freshdesk.updateTicket@1` — Freshdesk — Update ticket

Changes status, agent, group or fields on a Freshdesk ticket.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `freshdesk`) |
| `ticketId` | text, required | Ticket id |
| `status` | "" or "open" or "pending" or "resolved" or "closed", default `""` | Status |
| `priority` | "" or "low" or "medium" or "high" or "urgent", default `""` | Priority |
| `subject` | text, default `""` | Subject: A ticket created as an outbound e-mail will not let you change its subject or description. |
| `type` | text, default `""` | Type |
| `responderId` | text, default `""` | Assign to agent id — a value from the vendor's own list |
| `groupId` | text, default `""` | Group id — a value from the vendor's own list |
| `companyId` | text, default `""` | Company id |
| `tags` | text, default `""` | Tags: Comma-separated. Freshdesk REPLACES the whole tag list with what you send. |
| `customFields` | object of text, default `{}` | Custom fields: Only the fields you list here are changed. |
| `dueBy` | text, default `""` | Due by |
| `frDueBy` | text, default `""` | First response due by |

Output: `ticketId` number — Ticket id · `ticket` object — Ticket · `ticketUrl` string — Link to the ticket.

```json
{"type":"freshdesk.updateTicket","version":1,"label":"Update ticket","config":{"status":"","priority":"","subject":"","type":"","responderId":"","groupId":"","companyId":"","tags":"","customFields":{},"dueBy":"","frDueBy":"","connection":"<connection id>","ticketId":"<ticket id>"}}
```


---

## File: references/nodes/gemini.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Gemini nodes (`gemini.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `gemini.classify@1` — Gemini — Classify

Sorts a text into one of your labels with Gemini and sends the flow down that path.

Ports: one per row `id` in `labels`, plus `other` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Your own Google AI Studio API key. Every call runs on it and is billed by Google AI Studio to your account. — a connection id from `requires.connections` (provider `google-ai-studio`) |
| `model` | text, required | Model: The models your Google AI Studio connection listed when its key was saved. Refresh the connection to see a model Google AI Studio added since. Automatic retries only help when the model never started answering. — an AI model id |
| `text` | text, required | Text: What is being sorted. Everything here is sent to the model. |
| `labels` | list of {id: text, label: text, description?: text}, default `[]` | Labels: Each label becomes an output on the canvas. Because this step decides where the flow goes next, it cannot be turned off and it cannot continue on error — delete it instead. |
| `whenNoMatch` | "other" or "fail", default `"other"` | When nothing fits: Every text ends somewhere: down the Other path, or with the step failing. A label the model invents counts as nothing fitting. |
| `instructions` | text, default `""` | Instructions: Anything the model should know: what a borderline text should count as, or which label wins when two could fit. |
| `reasoning` | "default" or "low" or "medium" or "high", default `"default"` | Thinking: How hard Gemini may think before it answers. Model default leaves it to the model — most Gemini models think by default, and Gemini 3 always thinks a little; a level sets the thinking level on Gemini 3 and a thinking budget on Gemini 2.5. Thinking is billed by Google to your account as output tokens, on top of the answer, and the step gives it its own room on top of the answer's output ceiling, so a higher level needs no higher Maximum length. |

Output: shaped by the config.

```json
{"type":"gemini.classify","version":1,"label":"Classify","config":{"labels":[],"whenNoMatch":"other","instructions":"","reasoning":"default","connection":"<connection id>","model":"<model>","text":"<text>"}}
```

## `gemini.extract@1` — Gemini — Extract fields

Pulls the values you name out of a text or a document with Gemini, each typed.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Your own Google AI Studio API key. Every call runs on it and is billed by Google AI Studio to your account. — a connection id from `requires.connections` (provider `google-ai-studio`) |
| `model` | text, required | Model: The models your Google AI Studio connection listed when its key was saved. Refresh the connection to see a model Google AI Studio added since. Automatic retries only help when the model never started answering. — an AI model id |
| `source` | "text" or "file", default `"text"` | Read from: Text you point at with an expression, or a file in this workspace. |
| `text` | text, default `""` | Text: Everything here is sent to the model — point at the part that holds the values. — used when `source` is "text" |
| `file` | text, default `""` | File: Gemini reads a PDF or a JPEG, PNG, WebP, HEIC or HEIF image itself, tables and pictures included; Google does not list GIF, so convert a GIF first. Word, Excel, OpenDocument, HTML, XML and CSV files are read as text first, and plain text as it is. Up to 5 MB. — a file name, listed in `requires.files`; used when `source` is "file" |
| `fields` | list of {name: text, description?: text, type?: "text" or "number" or "boolean" or "date" or "text-list" or "number-list", required?: true/false}, optional | Fields: Add the fields you want back. A sentence saying where to find each one makes this much more accurate — “the total including VAT, near the bottom”. |
| `instructions` | text, default `""` | Instructions: Anything the model should know about the source before it reads it. |
| `whenMissing` | "empty" or "fail", default `"empty"` | When a field is missing: A value the source does not carry comes back empty. Failing instead stops the run on any field you marked required, and says which one. |
| `useJsonSchema` | true/false, default `false` | Use JSON Schema instead of fields: For an answer the field rows cannot describe — nesting, enums, a list of objects. It replaces the fields above entirely. |
| `schema` | object (any keys), default `{}` | Schema: A JSON Schema document. The answer is validated against it and one repair round is allowed before the step fails. — used when `useJsonSchema` is true |
| `reasoning` | "default" or "low" or "medium" or "high", default `"default"` | Thinking: How hard Gemini may think before it answers. Model default leaves it to the model — most Gemini models think by default, and Gemini 3 always thinks a little; a level sets the thinking level on Gemini 3 and a thinking budget on Gemini 2.5. Thinking is billed by Google to your account as output tokens, on top of the answer, and the step gives it its own room on top of the answer's output ceiling, so a higher level needs no higher Maximum length. |

Output: shaped by the config.

```json
{"type":"gemini.extract","version":1,"label":"Extract fields","config":{"source":"text","text":"","file":"","instructions":"","whenMissing":"empty","useJsonSchema":false,"schema":{},"reasoning":"default","connection":"<connection id>","model":"<model>","fields":[]}}
```

## `gemini.generate@1` — Gemini — Generate text

Asks Gemini to write something from your instructions and the flow's data.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Your own Google AI Studio API key. Every call runs on it and is billed by Google AI Studio to your account. — a connection id from `requires.connections` (provider `google-ai-studio`) |
| `model` | text, required | Model: The models your Google AI Studio connection listed when its key was saved. Refresh the connection to see a model Google AI Studio added since. Automatic retries only help when the model never started answering. — an AI model id |
| `task` | text, default `"Custom"` | What to do: Picks the standing instruction the model gets before your prompt. Complete a suggestion in your own words — “Translate to Dutch” — or choose Custom and say everything in the prompt. |
| `prompt` | text, required | Prompt: What you want, and the data it is about. Everything here is sent to the model, so point at the values you need and not at the whole payload. |
| `output` | "text" or "fields", default `"text"` | Output: Text answers one piece of writing. Fields turns the answer into a typed object, the way Extract fields does. |
| `fields` | list of {name: text, description?: text, type?: "text" or "number" or "boolean" or "date" or "text-list" or "number-list", required?: true/false}, default `[]` | Fields: What you want back, and a sentence saying where to find each one — that sentence is most of what makes the answer right. — used when `output` is "fields" |
| `creativity` | number, optional | Creativity: 0 to 1. Low keeps the answer close to what you asked for; high lets the model wander, which is rarely what a flow wants. Left empty, a Gemini 3 model runs at 1, as Google recommends: set lower, it can repeat itself or answer worse. |
| `maxLength` | number, default `1024` | Maximum length: In tokens — roughly three quarters of a word each. The model stops here, so a ceiling that is too low cuts the answer off mid-sentence. Gemini's thinking gets its own room on top of this number, so it caps the answer alone. |
| `reasoning` | "default" or "low" or "medium" or "high", default `"default"` | Thinking: How hard Gemini may think before it answers. Model default leaves it to the model — most Gemini models think by default, and Gemini 3 always thinks a little; a level sets the thinking level on Gemini 3 and a thinking budget on Gemini 2.5. Thinking is billed by Google to your account as output tokens, on top of the answer, and the step gives it its own room on top of the answer's output ceiling, so a higher level needs no higher Maximum length. |

Output: `text` string — Text · more fields follow from the config.

```json
{"type":"gemini.generate","version":1,"label":"Generate text","config":{"task":"Custom","output":"text","fields":[],"maxLength":1024,"reasoning":"default","connection":"<connection id>","model":"<model>","prompt":"<prompt>"}}
```


---

## File: references/nodes/graph.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Microsoft Graph nodes (`graph.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `graph.addGroupMember@1` — Microsoft Graph — Add group member

Adds a person, device or app to a Microsoft 365 or security group.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `GroupMember.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `groupId` | text, required | Group id |
| `memberId` | text, required | Member id: The object id of a user, or for a security group also of a group, device or service principal. A Microsoft 365 group takes users only. |

Output: `added` boolean — Added by this run · `alreadyMember` boolean — Was already a member · `group` string — Group id · `member` string — Member id.

```json
{"type":"graph.addGroupMember","version":1,"label":"Add group member","config":{"connection":"<connection id>","groupId":"<group id>","memberId":"<member id>"}}
```

## `graph.addGroupOwner@1` — Microsoft Graph — Add group owner

Makes a person or app an owner of a Microsoft 365 or security group.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `Group.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `groupId` | text, required | Group id |
| `ownerId` | text, required | Owner id: The object id of a user or service principal. An owner is not a member: add them as a member too if they should take part in the group's team. |

Output: `added` boolean — Added by this run · `alreadyOwner` boolean — Was already an owner · `group` string — Group id · `owner` string — Owner id.

```json
{"type":"graph.addGroupOwner","version":1,"label":"Add group owner","config":{"connection":"<connection id>","groupId":"<group id>","ownerId":"<owner id>"}}
```

## `graph.assignLicense@1` — Microsoft Graph — Assign license

Adds Microsoft 365 licenses to a person whose usage location is set.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `LicenseAssignment.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. Graph refuses a license to someone without a usage location. |
| `licenses` | text, required | Licenses to add: SKU ids, comma-separated. Every service plan in them is switched on; licenses the person already has stay. |

Output: `user` object — User · `user.id` string — Id · `user.displayName` string — Display name · `assignedLicenses` array — Licenses the user now has.

```json
{"type":"graph.assignLicense","version":1,"label":"Assign license","config":{"connection":"<connection id>","user":"<user>","licenses":"<licenses to add>"}}
```

## `graph.cancelEvent@1` — Microsoft Graph — Cancel calendar event

Cancels a meeting as its organizer and sends the attendees a cancellation with a note.

Ports: `out` · writes

Needs, on its connection: `Calendars.ReadWrite`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | Organizer: The meeting's organizer, by object id or sign-in address. For anyone else Graph answers 400: “You need to be an organizer to cancel a meeting.” |
| `calendarId` | text, default `""` | Calendar id: Leave empty for the user's default calendar. |
| `eventId` | text, required | Event id: The id a Create or List calendar events node answered. |
| `comment` | text, default `""` | Message to attendees: Sent with the cancellation. Leave empty to send none. |

Output: `cancelled` boolean — Cancelled · `event` string — Event id.

```json
{"type":"graph.cancelEvent","version":1,"label":"Cancel calendar event","config":{"calendarId":"","comment":"","connection":"<connection id>","user":"<organizer>","eventId":"<event id>"}}
```

## `graph.createEvent@1` — Microsoft Graph — Create calendar event

Creates a meeting in a Microsoft 365 calendar and invites people.

Ports: `out` · writes

Needs, on its connection: `Calendars.ReadWrite`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | Calendar of: A user's object id, or their sign-in address. |
| `calendarId` | text, default `""` | Calendar id: Leave empty for the user's default calendar. |
| `subject` | text, required | Subject |
| `bodyFormat` | "text" or "html", default `"text"` | Body format |
| `body` | text, default `""` | Body |
| `start` | text, required | Starts: ISO 8601 without a zone — the time zone below applies. |
| `end` | text, required | Ends |
| `timeZone` | text, default `"UTC"` | Time zone: An IANA name, e.g. Europe/Amsterdam. Defaults to UTC. |
| `location` | text, default `""` | Location |
| `attendees` | text, default `""` | Invite: Everyone here gets a real invitation. |
| `attendeeType` | "required" or "optional", default `"required"` | Invitation type |
| `isOnlineMeeting` | true/false, default `false` | Online meeting |

Output: `id` string — Event id · `webLink` string — Link in Outlook · `event` object — Event.

```json
{"type":"graph.createEvent","version":1,"label":"Create calendar event","config":{"calendarId":"","bodyFormat":"text","body":"","timeZone":"UTC","location":"","attendees":"","attendeeType":"required","isOnlineMeeting":false,"connection":"<connection id>","user":"<calendar of>","subject":"<subject>","start":"<starts>","end":"<ends>"}}
```

## `graph.createFolder@1` — Microsoft Graph — Create folder

Makes a new folder in OneDrive or SharePoint.

Ports: `out` · writes

Needs, on its connection: `Files.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `driveOf` | "user" or "group" or "site" or "drive", default `"user"` | Drive of |
| `owner` | text, required | Owner: The person's id or sign-in address, the group's id, the site's id or the drive's id — whichever Drive of names. |
| `folder` | text, default `""` | In folder: The folder to make it in: its id, or its path from the drive's root starting with /. Leave empty for the root. |
| `name` | text, required | Folder name: One name — a / would make it a path. |
| `conflictBehavior` | "use" or "rename" or "fail", default `"use"` | If the name is taken |

Output: `id` string — Folder id · `webUrl` string — Link in Microsoft 365 · `folder` object — Folder · `alreadyExisted` boolean — It was there already.

```json
{"type":"graph.createFolder","version":1,"label":"Create folder","config":{"driveOf":"user","folder":"","conflictBehavior":"use","connection":"<connection id>","owner":"<owner>","name":"<folder name>"}}
```

## `graph.createGroup@1` — Microsoft Graph — Create group

Creates a Microsoft 365 or security group in your directory.

Ports: `out` · writes

Needs, on its connection: `Group.Create`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `kind` | "unified" or "security", default `"unified"` | Kind |
| `displayName` | text, required | Name |
| `mailNickname` | text, required | Mail alias: At most 64 ASCII characters, without spaces or @ ( ) \ [ ] " ; : < > ,. A Microsoft 365 group's alias is unique in the organisation. |
| `description` | text, default `""` | Description |
| `visibility` | "" or "Private" or "Public", default `""` | Visibility — used when `kind` is "unified" |
| `owners` | text, default `""` | Owners: User ids, comma-separated. Name at least one: with this app's permission, a group without an owner is created anonymously and cannot be managed, and a Microsoft 365 group may get no SharePoint site. |
| `members` | text, default `""` | Members: User ids, comma-separated. Owners and members together: at most 20. |

Output: `id` string — Group id · `group` object — Group · `group.displayName` string — Display name.

```json
{"type":"graph.createGroup","version":1,"label":"Create group","config":{"kind":"unified","description":"","visibility":"","owners":"","members":"","connection":"<connection id>","displayName":"<name>","mailNickname":"<mail alias>"}}
```

## `graph.createListItem@1` — Microsoft Graph — Create SharePoint list item

Adds a row to a SharePoint list, with the column values you give.

Ports: `out` · writes

Needs, on its connection: `Sites.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `siteId` | text, required | Site id: The id Get SharePoint site answered — host name, site GUID and web GUID. |
| `listId` | text, required | List: The list's id, or its title. |
| `fields` | any JSON, optional | Column values: Internal column name to value, as JSON — or one {{ }} expression that produces the object. A space in a column name is _x0020_. |

Output: `id` string — Item id · `item` object — Item · `item.fields` object — Column values.

```json
{"type":"graph.createListItem","version":1,"label":"Create SharePoint list item","config":{"connection":"<connection id>","siteId":"<site id>","listId":"<list>"}}
```

## `graph.createPlannerTask@1` — Microsoft Graph — Create Planner task

Adds a task to a Planner plan, with its bucket, people and due date.

Ports: `out` · writes

Needs, on its connection: `Tasks.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `planId` | text, required | Plan id |
| `title` | text, required | Title |
| `bucketId` | text, default `""` | Bucket id: The column the task goes in — it must belong to the plan. |
| `assignTo` | text, default `""` | Assign to: User object ids, comma-separated — the id Get user answers. |
| `startDateTime` | text, default `""` | Starts: ISO 8601 in UTC. |
| `dueDateTime` | text, default `""` | Due |
| `priority` | "" or "urgent" or "important" or "medium" or "low", default `""` | Priority |

Output: `id` string — Task id · `etag` string — ETag — what Update Planner task checks against · `task` object — Task.

```json
{"type":"graph.createPlannerTask","version":1,"label":"Create Planner task","config":{"bucketId":"","assignTo":"","startDateTime":"","dueDateTime":"","priority":"","connection":"<connection id>","planId":"<plan id>","title":"<title>"}}
```

## `graph.createUser@1` — Microsoft Graph — Create user

Creates a work account in your Microsoft 365 directory, with a first password.

Ports: `out` · writes

Needs, on its connection: `User.Create`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `displayName` | text, required | Display name |
| `userPrincipalName` | text, required | Sign-in address: alias@domain — the domain must be one your tenant has verified. |
| `mailNickname` | text, required | Mail alias: The part of their mail address before the @. |
| `auth.password` | text, required | First password: Never printed, never logged. It must meet your tenant's password policy. — write `{{ secret.NAME }}`, never the value |
| `forceChangePasswordNextSignIn` | true/false, default `true` | Change password at first sign-in |
| `accountEnabled` | true/false, default `true` | Can sign in: Off creates the account blocked from signing in. |
| `givenName` | text, default `""` | First name |
| `surname` | text, default `""` | Last name |
| `jobTitle` | text, default `""` | Job title |
| `department` | text, default `""` | Department |
| `usageLocation` | text, default `""` | Usage location: A two-letter country code, e.g. NL. A licence can only be assigned once it is set. |

Output: `id` string — User id · `userPrincipalName` string — Sign-in address · `user` object — User · `user.displayName` string — Display name.

```json
{"type":"graph.createUser","version":1,"label":"Create user","config":{"forceChangePasswordNextSignIn":true,"accountEnabled":true,"givenName":"","surname":"","jobTitle":"","department":"","usageLocation":"","connection":"<connection id>","displayName":"<display name>","userPrincipalName":"<sign-in address>","mailNickname":"<mail alias>","auth":{"password":"<first password>"}}}
```

## `graph.deleteDriveItem@1` — Microsoft Graph — Delete drive item

Deletes a file or folder from OneDrive or SharePoint.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `Files.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `driveOf` | "user" or "group" or "site" or "drive", default `"user"` | Drive of |
| `owner` | text, required | Owner: The person's id or sign-in address, the group's id, the site's id or the drive's id — whichever Drive of names. |
| `item` | text, required | File or folder: Its id, or its path from the drive's root starting with /. A folder goes with everything in it, to the recycle bin. |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — It was already gone · `item` string — File or folder.

```json
{"type":"graph.deleteDriveItem","version":1,"label":"Delete drive item","config":{"driveOf":"user","connection":"<connection id>","owner":"<owner>","item":"<file or folder>"}}
```

## `graph.deleteEvent@1` — Microsoft Graph — Delete calendar event

Deletes a Microsoft 365 calendar event; to tell attendees why, use Cancel event.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `Calendars.ReadWrite`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | Calendar of: A user's object id, or their sign-in address. |
| `calendarId` | text, default `""` | Calendar id: Leave empty for the user's default calendar. |
| `eventId` | text, required | Event id: The id a Create or List calendar events node answered. |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — It was already gone · `event` string — Event id.

```json
{"type":"graph.deleteEvent","version":1,"label":"Delete calendar event","config":{"calendarId":"","connection":"<connection id>","user":"<calendar of>","eventId":"<event id>"}}
```

## `graph.deleteGroup@1` — Microsoft Graph — Delete group

Deletes a group from your Microsoft 365 directory.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `Group.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `groupId` | text, required | Group id |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — It was already gone · `group` string — Group id.

```json
{"type":"graph.deleteGroup","version":1,"label":"Delete group","config":{"connection":"<connection id>","groupId":"<group id>"}}
```

## `graph.deleteListItem@1` — Microsoft Graph — Delete SharePoint list item

Deletes one row from a SharePoint list.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `Sites.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `siteId` | text, required | Site id: The id Get SharePoint site answered — host name, site GUID and web GUID. |
| `listId` | text, required | List: The list's id, or its title. |
| `itemId` | text, required | Item id |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — It was already gone · `item` string — Item id.

```json
{"type":"graph.deleteListItem","version":1,"label":"Delete SharePoint list item","config":{"connection":"<connection id>","siteId":"<site id>","listId":"<list>","itemId":"<item id>"}}
```

## `graph.deleteUser@1` — Microsoft Graph — Delete user

Deletes a person's account from your Microsoft 365 directory.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `User.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — It was already gone · `user` string — User.

```json
{"type":"graph.deleteUser","version":1,"label":"Delete user","config":{"connection":"<connection id>","user":"<user>"}}
```

## `graph.downloadFile@1` — Microsoft Graph — Download file

Downloads the contents of one file from OneDrive or SharePoint.

Ports: `out` · reads

Needs, on its connection: `Files.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `driveOf` | "user" or "group" or "site" or "drive", default `"user"` | Drive of |
| `owner` | text, required | Owner: The person's id or sign-in address, the group's id, the site's id or the drive's id — whichever Drive of names. |
| `item` | text, required | File: The file's id, or its path from the drive's root starting with /. The run's parse limit bounds the size. |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"graph.downloadFile","version":1,"label":"Download file","config":{"driveOf":"user","connection":"<connection id>","owner":"<owner>","item":"<file>"}}
```

## `graph.forwardMessage@1` — Microsoft Graph — Forward message

Forwards an email from a Microsoft 365 mailbox to other people.

Ports: `out` · writes

Needs, on its connection: `Mail.Send`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `mailbox` | text, required | Mailbox: The mailbox this node works in — a user's sign-in address, or their object id. Your app needs Mail.Send for it. |
| `messageId` | text, required | Message id: The id a list of messages answered, as Graph gave it. |
| `to` | text, required | To |
| `comment` | text, default `""` | Comment: Text above the forwarded message. Leave empty to forward it as it is. |

Output: `sent` boolean — Sent · `message` string — Forwarded message id · `recipients` array — Recipients.

```json
{"type":"graph.forwardMessage","version":1,"label":"Forward message","config":{"comment":"","connection":"<connection id>","mailbox":"<mailbox>","messageId":"<message id>","to":"<to>"}}
```

## `graph.getDriveItem@1` — Microsoft Graph — Get drive item

Reads the details of one file or folder in OneDrive or SharePoint.

Ports: `out` · reads

Needs, on its connection: `Files.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `driveOf` | "user" or "group" or "site" or "drive", default `"user"` | Drive of |
| `owner` | text, required | Owner: The person's id or sign-in address, the group's id, the site's id or the drive's id — whichever Drive of names. |
| `item` | text, required | File or folder: Its id, or its path from the drive's root starting with / — / alone is the root. |
| `select` | text, default `""` | Fields: Comma-separated property names. Empty answers Microsoft 365's default set. |

Output: `item` object — File or folder · `item.id` string — Id · `item.name` string — Name · `item.webUrl` string — Link in Microsoft 365.

```json
{"type":"graph.getDriveItem","version":1,"label":"Get drive item","config":{"driveOf":"user","select":"","connection":"<connection id>","owner":"<owner>","item":"<file or folder>"}}
```

## `graph.getEvent@1` — Microsoft Graph — Get calendar event

Reads one meeting from a Microsoft 365 calendar, with its body and attendees.

Ports: `out` · reads

Needs, on its connection: `Calendars.Read`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | Calendar of: A user's object id, or their sign-in address. |
| `calendarId` | text, default `""` | Calendar id: Leave empty for the user's default calendar. |
| `eventId` | text, required | Event id: The id a Create or List calendar events node answered. |
| `select` | text, default `""` | Fields: Comma-separated property names. Empty reads them all. |
| `timeZone` | text, default `""` | Time zone: The time zone the times are given in, e.g. Europe/Amsterdam. Empty means UTC. |
| `bodyContentType` | "" or "text" or "html", default `""` | Body format |

Output: `event` object — Event · `event.id` string — Event id · `event.subject` string — Subject.

```json
{"type":"graph.getEvent","version":1,"label":"Get calendar event","config":{"calendarId":"","select":"","timeZone":"","bodyContentType":"","connection":"<connection id>","user":"<calendar of>","eventId":"<event id>"}}
```

## `graph.getGroup@1` — Microsoft Graph — Get group

Reads one group from your Microsoft 365 directory.

Ports: `out` · reads

Needs, on its connection: `Group.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `groupId` | text, required | Group id: The group's object id. |
| `select` | text, default `"id,displayName,description,mail,mailNickname,groupTypes,visibility"` | Fields: Comma-separated property names, including ones a list leaves out. |

Output: `group` object — Group · `group.id` string — Id · `group.displayName` string — Display name.

```json
{"type":"graph.getGroup","version":1,"label":"Get group","config":{"select":"id,displayName,description,mail,mailNickname,groupTypes,visibility","connection":"<connection id>","groupId":"<group id>"}}
```

## `graph.getGroupChanges@1` — Microsoft Graph — Get group changes

Finds the groups and memberships that changed in your directory since the last check.

Ports: `out` · reads

Needs, on its connection: `Group.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `deltaLink` | text, default `""` | Delta link: Leave empty to read every group once. Put the delta link a previous run answered here to read only what changed since. |
| `select` | text, default `"id,displayName,mail,groupTypes,members"` | Fields: Comma-separated property names to track; members adds who joined and left as members@delta. Used when Delta link is empty. |
| `filter` | text, default `""` | Filter: Only id eq '…' terms joined with or, at most 50 ids. Used when Delta link is empty. |
| `minimal` | true/false, default `false` | Changed properties only: With a delta link, a group carries only the properties that changed. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. A round still going then answers the link to carry on from. |

Output: `groups` array — Changed groups · `count` number — Changes in this answer · `hasMore` boolean — More changes are waiting behind the link · `deltaLink` string — Delta link for the next run.

```json
{"type":"graph.getGroupChanges","version":1,"label":"Get group changes","config":{"deltaLink":"","select":"id,displayName,mail,groupTypes,members","filter":"","minimal":false,"maxPages":10,"connection":"<connection id>"}}
```

## `graph.getManager@1` — Microsoft Graph — Get manager

Reads who a person reports to in your Microsoft 365 directory.

Ports: `out` · reads

Needs, on its connection: `User.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `select` | text, default `"id,displayName,mail,userPrincipalName,jobTitle"` | Manager fields: Comma-separated property names of the manager. |

Output: `hasManager` boolean — Has a manager · `manager` object — Manager · `manager.id` string — Manager id · `manager.displayName` string — Manager name.

```json
{"type":"graph.getManager","version":1,"label":"Get manager","config":{"select":"id,displayName,mail,userPrincipalName,jobTitle","connection":"<connection id>","user":"<user>"}}
```

## `graph.getMessage@1` — Microsoft Graph — Get message

Reads one email from a Microsoft 365 mailbox, its body included.

Ports: `out` · reads

Needs, on its connection: `Mail.Read`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `mailbox` | text, required | Mailbox: The mailbox this node works in — a user's sign-in address, or their object id. Your app needs Mail.Read for it. |
| `messageId` | text, required | Message id: The id a list of messages answered, as Graph gave it. |
| `select` | text, default `"id,subject,from,toRecipients,receivedDateTime,isRead,body"` | Fields: Comma-separated property names. Leave empty for every property. |
| `bodyFormat` | "" or "text" or "html", default `""` | Body format |

Output: `message` object — Message · `message.id` string — Id · `message.subject` string — Subject.

```json
{"type":"graph.getMessage","version":1,"label":"Get message","config":{"select":"id,subject,from,toRecipients,receivedDateTime,isRead,body","bodyFormat":"","connection":"<connection id>","mailbox":"<mailbox>","messageId":"<message id>"}}
```

## `graph.getSchedule@1` — Microsoft Graph — Get free/busy schedule

Finds when people and rooms in Microsoft 365 are free or busy over a time window.

Ports: `out` · reads

Needs, on its connection: `Calendars.ReadBasic`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | Ask as: The user whose calendar asks, by object id or sign-in address. |
| `schedules` | text, required | People and rooms: The addresses of users, distribution lists or rooms to read free/busy for. |
| `start` | text, required | From: ISO 8601 without a zone — the time zone below applies. |
| `end` | text, required | Until |
| `timeZone` | text, default `"UTC"` | Time zone: An IANA name, e.g. Europe/Amsterdam. Defaults to UTC. The answer's times are in it too. |
| `interval` | number, default `30` | Slot length: Minutes per character of the availability view, 5 to 1440. A calendar slot holding more than 1000 entries is refused by Graph. |

Output: `schedules` array — One schedule per address · `count` number — Schedules in this answer.

```json
{"type":"graph.getSchedule","version":1,"label":"Get free/busy schedule","config":{"timeZone":"UTC","interval":30,"connection":"<connection id>","user":"<ask as>","schedules":"<people and rooms>","start":"<from>","end":"<until>"}}
```

## `graph.getSite@1` — Microsoft Graph — Get SharePoint site

Reads one SharePoint site by its address or id, or the tenant's root site.

Ports: `out` · reads

Needs, on its connection: `Sites.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `hostname` | text, default `""` | Host name |
| `path` | text, default `""` | Path: The site's path after the host name. Leave the host name and path empty for the tenant's root site. |
| `siteId` | text, default `""` | Site id: Instead of a path: the id a site read answered — host name, site GUID and web GUID, comma-separated. |

Output: `id` string — Site id · `webUrl` string — Address in SharePoint · `site` object — Site · `site.displayName` string — Display name.

```json
{"type":"graph.getSite","version":1,"label":"Get SharePoint site","config":{"hostname":"","path":"","siteId":"","connection":"<connection id>"}}
```

## `graph.getUser@1` — Microsoft Graph — Get user

Reads one person from your Microsoft 365 directory.

Ports: `out` · reads

Needs, on its connection: `User.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `select` | text, default `"id,displayName,mail,userPrincipalName,jobTitle,department"` | Fields: Comma-separated property names. |

Output: `user` object — User · `user.id` string — Id · `user.displayName` string — Display name.

```json
{"type":"graph.getUser","version":1,"label":"Get user","config":{"select":"id,displayName,mail,userPrincipalName,jobTitle,department","connection":"<connection id>","user":"<user>"}}
```

## `graph.getUserChanges@1` — Microsoft Graph — Get user changes

Finds the people added, edited or removed in your directory since the last check.

Ports: `out` · reads

Needs, on its connection: `User.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `deltaLink` | text, default `""` | Delta link: Leave empty to read everyone once. Put the delta link a previous run answered here to read only what changed since. |
| `select` | text, default `"id,displayName,mail,userPrincipalName,accountEnabled"` | Fields: Comma-separated property names to track. Used when Delta link is empty — the link carries them after that. |
| `filter` | text, default `""` | Filter: Only id eq '…' terms joined with or, at most 50 ids. Used when Delta link is empty. |
| `minimal` | true/false, default `false` | Changed properties only: With a delta link, a person carries only the properties that changed. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. A round still going then answers the link to carry on from. |

Output: `users` array — Changed people · `count` number — Changes in this answer · `hasMore` boolean — More changes are waiting behind the link · `deltaLink` string — Delta link for the next run.

```json
{"type":"graph.getUserChanges","version":1,"label":"Get user changes","config":{"deltaLink":"","select":"id,displayName,mail,userPrincipalName,accountEnabled","filter":"","minimal":false,"maxPages":10,"connection":"<connection id>"}}
```

## `graph.getUserPhoto@1` — Microsoft Graph — Get user photo

Downloads a person's Microsoft 365 profile picture as a file.

Ports: `out` · reads

Needs, on its connection: `ProfilePhoto.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `size` | "" or "48x48" or "64x64" or "96x96" or "120x120" or "240x240" or "360x360" or "432x432" or "504x504" or "648x648", default `""` | Size: Pixels. A size larger than the photo the user uploaded is not available. |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"graph.getUserPhoto","version":1,"label":"Get user photo","config":{"size":"","connection":"<connection id>","user":"<user>"}}
```

## `graph.listAttachments@1` — Microsoft Graph — List attachments

Lists the files attached to an email in a Microsoft 365 mailbox.

Ports: `out` · reads

Needs, on its connection: `Mail.Read`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `mailbox` | text, required | Mailbox: The mailbox this node works in — a user's sign-in address, or their object id. Your app needs Mail.Read for it. |
| `messageId` | text, required | Message id: The id a list of messages answered, as Graph gave it. |
| `select` | text, default `"id,name,contentType,size,isInline"` | Fields: Comma-separated property names. Leave empty for every property, a file's contentBytes among them — its contents as base64, megabytes for a large file. |

Output: `attachments` array — Attachments · `count` number — Attachments on the message.

```json
{"type":"graph.listAttachments","version":1,"label":"List attachments","config":{"select":"id,name,contentType,size,isInline","connection":"<connection id>","mailbox":"<mailbox>","messageId":"<message id>"}}
```

## `graph.listDirectReports@1` — Microsoft Graph — List direct reports

Lists the people who report to a person in your Microsoft 365 directory.

Ports: `out` · reads

Needs, on its connection: `User.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `select` | text, default `"id,displayName,mail,userPrincipalName,jobTitle"` | Fields: Comma-separated property names. |
| `filter` | text, default `""` | Filter: Read from Graph's search index, which can lag recent changes by a moment. |
| `top` | number, default `100` | Reports per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `reports` array — Direct reports · `count` number — Reports in this answer · `hasMore` boolean — More reports are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listDirectReports","version":1,"label":"List direct reports","config":{"select":"id,displayName,mail,userPrincipalName,jobTitle","filter":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","user":"<user>"}}
```

## `graph.listEvents@2` — Microsoft Graph — List calendar events

Lists the events in a Microsoft 365 calendar.

Ports: `out` · reads

Needs, on its connection: `Calendars.Read`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `calendarId` | text, default `""` | Calendar id: Leave empty for the user's default calendar. |
| `select` | text, default `"id,subject,start,end,location,organizer,isAllDay"` | Fields |
| `filter` | text, default `""` | Filter: An OData filter. Recurrence cannot be filtered on — expanded occurrences need a calendar view, which this node does not read. |
| `orderBy` | text, default `""` | Sort by |
| `top` | number, default `100` | Events per call: 1 to 999. |
| `timeZone` | text, default `""` | Time zone: The time zone the times are given in, e.g. Europe/Amsterdam. Empty means UTC. |
| `bodyContentType` | "" or "text" or "html", default `""` | Body format |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link; refuses rather than truncating at Stop after. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `events` array — Events · `count` number — Events in this answer · `hasMore` boolean — More events are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listEvents","version":2,"label":"List calendar events","config":{"calendarId":"","select":"id,subject,start,end,location,organizer,isAllDay","filter":"","orderBy":"","top":100,"timeZone":"","bodyContentType":"","returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","user":"<user>"}}
```

### Older version `graph.listEvents@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `calendarId` | text, default `""` | Calendar id: Leave empty for the user's default calendar. |
| `select` | text, default `"id,subject,start,end,location,organizer,isAllDay"` | Fields |
| `filter` | text, default `""` | Filter: An OData filter. Recurrence cannot be filtered on — expanded occurrences need a calendar view, which this node does not read. |
| `orderBy` | text, default `""` | Sort by |
| `top` | number, default `100` | Events per call: 1 to 999. |
| `timeZone` | text, default `""` | Time zone: The time zone the times are given in, e.g. Europe/Amsterdam. Empty means UTC. |
| `bodyContentType` | "" or "text" or "html", default `""` | Body format |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link; refuses rather than truncating at Stop after. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"calendarId":"","select":"id,subject,start,end,location,organizer,isAllDay","filter":"","orderBy":"","top":100,"timeZone":"","bodyContentType":"","returnAll":false,"maxPages":10,"connection":"<connection id>","user":"<user>"}
{"calendarId":"","select":"id,subject,start,end,location,organizer,isAllDay","filter":"","orderBy":"","top":100,"timeZone":"","bodyContentType":"","returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","user":"<user>"}
```

## `graph.listFolderItems@1` — Microsoft Graph — List folder items

Lists the files and folders inside a OneDrive or SharePoint folder.

Ports: `out` · reads

Needs, on its connection: `Files.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `driveOf` | "user" or "group" or "site" or "drive", default `"user"` | Drive of |
| `owner` | text, required | Owner: The person's id or sign-in address, the group's id, the site's id or the drive's id — whichever Drive of names. |
| `folder` | text, default `""` | Folder: The folder's id, or its path from the drive's root starting with /. Leave empty for the root. |
| `select` | text, default `""` | Fields: Comma-separated property names. Empty answers Microsoft 365's default set. |
| `orderby` | text, default `""` | Sort by |
| `top` | number, default `100` | Items per call: 1 to 999. Microsoft 365's own page is 200 items. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Files and folders · `count` number — Items in this answer · `hasMore` boolean — More items are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listFolderItems","version":1,"label":"List folder items","config":{"driveOf":"user","folder":"","select":"","orderby":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","owner":"<owner>"}}
```

## `graph.listGroupMembers@2` — Microsoft Graph — List group members

Lists the members of a Microsoft 365 group.

Ports: `out` · reads

Needs, on its connection: `GroupMember.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `groupId` | text, required | Group id |
| `memberType` | "" or "user" or "group" or "servicePrincipal" or "device" or "orgContact", default `""` | Member type |
| `select` | text, default `"id,displayName,mail,userPrincipalName"` | Fields |
| `filter` | text, default `""` | Filter |
| `search` | text, default `""` | Search |
| `top` | number, default `100` | Members per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. In v1.0 service principals are not listed — the vendor's own known issue. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `members` array — Members · `count` number — Members in this answer · `hasMore` boolean — More members are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listGroupMembers","version":2,"label":"List group members","config":{"memberType":"","select":"id,displayName,mail,userPrincipalName","filter":"","search":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","groupId":"<group id>"}}
```

### Older version `graph.listGroupMembers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `groupId` | text, required | Group id |
| `memberType` | "" or "user" or "group" or "servicePrincipal" or "device" or "orgContact", default `""` | Member type |
| `select` | text, default `"id,displayName,mail,userPrincipalName"` | Fields |
| `filter` | text, default `""` | Filter |
| `search` | text, default `""` | Search |
| `top` | number, default `100` | Members per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. In v1.0 service principals are not listed — the vendor's own known issue. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"memberType":"","select":"id,displayName,mail,userPrincipalName","filter":"","search":"","top":100,"returnAll":false,"maxPages":10,"connection":"<connection id>","groupId":"<group id>"}
{"memberType":"","select":"id,displayName,mail,userPrincipalName","filter":"","search":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","groupId":"<group id>"}
```

## `graph.listGroupOwners@1` — Microsoft Graph — List group owners

Lists the people and apps who own a Microsoft 365 or security group.

Ports: `out` · reads

Needs, on its connection: `GroupMember.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `groupId` | text, required | Group id |
| `select` | text, default `"id,displayName,mail,userPrincipalName"` | Fields: Comma-separated property names. |
| `filter` | text, default `""` | Filter |
| `search` | text, default `""` | Search: Matches words in the display name or description only. |
| `top` | number, default `100` | Owners per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `owners` array — Owners · `count` number — Owners in this answer · `hasMore` boolean — More owners are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listGroupOwners","version":1,"label":"List group owners","config":{"select":"id,displayName,mail,userPrincipalName","filter":"","search":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","groupId":"<group id>"}}
```

## `graph.listGroups@1` — Microsoft Graph — List groups

Lists the groups in your Microsoft 365 directory.

Ports: `out` · reads

Needs, on its connection: `Group.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `select` | text, default `"id,displayName,description,mail,groupTypes,securityEnabled"` | Fields: Comma-separated property names. |
| `filter` | text, default `""` | Filter: An OData filter. Microsoft 365 groups only: groupTypes/any(c:c eq 'Unified'). |
| `search` | text, default `""` | Search: Matches words in the display name or description only. |
| `orderby` | text, default `""` | Sort by |
| `top` | number, default `100` | Groups per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. A group made or deleted a moment ago may not show yet. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `groups` array — Groups · `count` number — Groups in this answer · `hasMore` boolean — More groups are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listGroups","version":1,"label":"List groups","config":{"select":"id,displayName,description,mail,groupTypes,securityEnabled","filter":"","search":"","orderby":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}}
```

## `graph.listListItems@1` — Microsoft Graph — List SharePoint list items

Lists the rows of a SharePoint list, with the columns you choose.

Ports: `out` · reads

Needs, on its connection: `Sites.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `siteId` | text, required | Site id: The id Get SharePoint site answered — host name, site GUID and web GUID. |
| `listId` | text, required | List: The list's id, or its title. |
| `fields` | text, default `""` | Columns: Internal column names, comma-separated — a space is _x0020_. Leave empty for every column. |
| `filter` | text, default `""` | Filter: Filters best on an indexed column, and on one indexed column at a time. |
| `allowNonIndexed` | true/false, default `false` | Allow filters on columns that are not indexed: Without an index SharePoint scans the whole list, and on a large list it may refuse the read at random — index the column for a read that always works. |
| `top` | number, default `100` | Items per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Items · `count` number — Items in this answer · `hasMore` boolean — More items are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listListItems","version":1,"label":"List SharePoint list items","config":{"fields":"","filter":"","allowNonIndexed":false,"top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","siteId":"<site id>","listId":"<list>"}}
```

## `graph.listLists@1` — Microsoft Graph — List SharePoint lists

Lists the lists and document libraries of one SharePoint site.

Ports: `out` · reads

Needs, on its connection: `Sites.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `siteId` | text, required | Site id: The id Get SharePoint site answered — host name, site GUID and web GUID. |
| `select` | text, default `"id,displayName,name,webUrl,list"` | Fields: Comma-separated property names. Add system to include the lists SharePoint hides. |
| `top` | number, default `100` | Lists per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `lists` array — Lists · `count` number — Lists in this answer · `hasMore` boolean — More lists are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listLists","version":1,"label":"List SharePoint lists","config":{"select":"id,displayName,name,webUrl,list","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","siteId":"<site id>"}}
```

## `graph.listMailFolders@1` — Microsoft Graph — List mail folders

Lists the folders of a Microsoft 365 mailbox, or those inside one folder.

Ports: `out` · reads

Needs, on its connection: `Mail.ReadBasic.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `mailbox` | text, required | Mailbox: The mailbox this node works in — a user's sign-in address, or their object id. Your app needs Mail.ReadBasic.All for it. |
| `parent` | text, default `""` | Inside folder: A folder id, or a well-known name such as inbox. Leave empty for the folders at the top. |
| `includeHidden` | true/false, default `false` | Include hidden folders |
| `select` | text, default `"id,displayName,parentFolderId,childFolderCount,unreadItemCount,totalItemCount"` | Fields |
| `filter` | text, default `""` | Filter |
| `top` | number, default `100` | Folders per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link; refuses rather than truncating at Stop after. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `folders` array — Folders · `count` number — Folders in this answer · `hasMore` boolean — More folders are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listMailFolders","version":1,"label":"List mail folders","config":{"parent":"","includeHidden":false,"select":"id,displayName,parentFolderId,childFolderCount,unreadItemCount,totalItemCount","filter":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","mailbox":"<mailbox>"}}
```

## `graph.listMessages@1` — Microsoft Graph — List messages

Lists the messages in a Microsoft 365 mailbox or one of its folders.

Ports: `out` · reads

Needs, on its connection: `Mail.Read`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `mailbox` | text, required | Mailbox: The mailbox this node works in — a user's sign-in address, or their object id. Your app needs Mail.Read for it. |
| `folder` | text, default `""` | Folder: A folder id, or a well-known name such as inbox. Leave empty for every folder. |
| `select` | text, default `"id,subject,from,receivedDateTime,isRead,hasAttachments,bodyPreview"` | Fields: Comma-separated property names. Add body for the whole text of each message. |
| `filter` | text, default `""` | Filter: An OData filter. With Sort by, the sorted properties come first here, in the same order — Graph's own rule. |
| `search` | text, default `""` | Search: Words to find, in double quotes. Graph cannot combine it with Filter or Sort by. |
| `orderBy` | text, default `""` | Sort by |
| `bodyFormat` | "" or "text" or "html", default `""` | Body format |
| `top` | number, default `100` | Messages per call: 1 to 1000. Large pages of whole messages can time out — narrow Fields first. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link; refuses rather than truncating at Stop after. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `messages` array — Messages · `count` number — Messages in this answer · `hasMore` boolean — More messages are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listMessages","version":1,"label":"List messages","config":{"folder":"","select":"id,subject,from,receivedDateTime,isRead,hasAttachments,bodyPreview","filter":"","search":"","orderBy":"","bodyFormat":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","mailbox":"<mailbox>"}}
```

## `graph.listPlanTasks@1` — Microsoft Graph — List Planner plan tasks

Lists the tasks of one Planner plan, with their progress and people.

Ports: `out` · reads

Needs, on its connection: `Tasks.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `planId` | text, required | Plan id |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `tasks` array — Tasks · `count` number — Tasks in this answer · `hasMore` boolean — More tasks are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listPlanTasks","version":1,"label":"List Planner plan tasks","config":{"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","planId":"<plan id>"}}
```

## `graph.listSubscribedSkus@1` — Microsoft Graph — List subscribed SKUs

Lists the Microsoft 365 products your organization bought, with seats used.

Ports: `out` · reads

Needs, on its connection: `LicenseAssignment.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `select` | text, default `""` | Fields: Comma-separated property names. Leave empty for all of them. |

Output: `skus` array — Subscribed SKUs · `count` number — SKUs in this answer.

```json
{"type":"graph.listSubscribedSkus","version":1,"label":"List subscribed SKUs","config":{"select":"","connection":"<connection id>"}}
```

## `graph.listUserGroups@1` — Microsoft Graph — List user groups

Lists the groups and roles a person directly belongs to in Microsoft 365.

Ports: `out` · reads

Needs, on its connection: `Directory.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `memberType` | "" or "group" or "directoryRole" or "administrativeUnit", default `""` | Membership type |
| `select` | text, default `"id,displayName,description,mail"` | Fields: Comma-separated property names. |
| `filter` | text, default `""` | Filter |
| `search` | text, default `""` | Search: Matches words in the display name. |
| `top` | number, default `100` | Groups per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. A type, filter or search reads Graph's search index, which can lag recent changes by a moment. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `groups` array — Groups · `count` number — Groups in this answer · `hasMore` boolean — More groups are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listUserGroups","version":1,"label":"List user groups","config":{"memberType":"","select":"id,displayName,description,mail","filter":"","search":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","user":"<user>"}}
```

## `graph.listUsers@2` — Microsoft Graph — List users

Lists the people in your Microsoft 365 directory, with filters.

Ports: `out` · reads

Needs, on its connection: `User.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `select` | text, default `"id,displayName,mail,userPrincipalName,jobTitle"` | Fields: Comma-separated property names. Asking for fewer is faster. |
| `filter` | text, default `""` | Filter: An OData filter, e.g. department eq 'Sales'. Needs ConsistencyLevel, which this node sends for you. |
| `orderBy` | text, default `""` | Sort by |
| `search` | text, default `""` | Search: A search phrase, e.g. "displayName:Sales". Wrap it in double quotes. |
| `top` | number, default `100` | Users per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link until it runs out. It refuses rather than handing back a short list when Stop after is reached. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. Reaching it fails the node rather than truncating. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `users` array — Users · `count` number — Users in this answer · `hasMore` boolean — More users are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.listUsers","version":2,"label":"List users","config":{"select":"id,displayName,mail,userPrincipalName,jobTitle","filter":"","orderBy":"","search":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}}
```

### Older version `graph.listUsers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `select` | text, default `"id,displayName,mail,userPrincipalName,jobTitle"` | Fields: Comma-separated property names. Asking for fewer is faster. |
| `filter` | text, default `""` | Filter: An OData filter, e.g. department eq 'Sales'. Needs ConsistencyLevel, which this node sends for you. |
| `orderBy` | text, default `""` | Sort by |
| `search` | text, default `""` | Search: A search phrase, e.g. "displayName:Sales". Wrap it in double quotes. |
| `top` | number, default `100` | Users per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link until it runs out. It refuses rather than handing back a short list when Stop after is reached. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. Reaching it fails the node rather than truncating. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"select":"id,displayName,mail,userPrincipalName,jobTitle","filter":"","orderBy":"","search":"","top":100,"returnAll":false,"maxPages":10,"connection":"<connection id>"}
{"select":"id,displayName,mail,userPrincipalName,jobTitle","filter":"","orderBy":"","search":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}
```

## `graph.moveMessage@1` — Microsoft Graph — Move message

Moves an email to another folder of its Microsoft 365 mailbox.

Ports: `out` · writes

Needs, on its connection: `Mail.ReadWrite`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `mailbox` | text, required | Mailbox: The mailbox this node works in — a user's sign-in address, or their object id. Your app needs Mail.ReadWrite for it. |
| `messageId` | text, required | Message id: The id a list of messages answered, as Graph gave it. |
| `destination` | text, required | Move to: A folder id, or a well-known name such as archive. The moved message gets a new id — use the one this node answers. |

Output: `id` string — New message id · `message` object — Moved message · `message.subject` string — Subject.

```json
{"type":"graph.moveMessage","version":1,"label":"Move message","config":{"connection":"<connection id>","mailbox":"<mailbox>","messageId":"<message id>","destination":"<move to>"}}
```

## `graph.removeGroupMember@1` — Microsoft Graph — Remove group member

Removes someone from a group while keeping their account in the directory.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `GroupMember.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `groupId` | text, required | Group id |
| `memberId` | text, required | Member id: The member's object id. |

Output: `removed` boolean — Removed by this run · `alreadyGone` boolean — Was no longer a member · `group` string — Group id · `member` string — Member id.

```json
{"type":"graph.removeGroupMember","version":1,"label":"Remove group member","config":{"connection":"<connection id>","groupId":"<group id>","memberId":"<member id>"}}
```

## `graph.removeLicense@1` — Microsoft Graph — Remove license

Removes Microsoft 365 licenses from a person, freeing those seats for someone else.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `LicenseAssignment.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `licenses` | text, required | Licenses to remove: SKU ids, comma-separated. A license the person has through a group stays; remove them from the group instead. |

Output: `user` object — User · `user.id` string — Id · `user.displayName` string — Display name · `assignedLicenses` array — Licenses the user now has.

```json
{"type":"graph.removeLicense","version":1,"label":"Remove license","config":{"connection":"<connection id>","user":"<user>","licenses":"<licenses to remove>"}}
```

## `graph.removeManager@1` — Microsoft Graph — Remove manager

Removes the manager a person reports to in your Microsoft 365 directory.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `User.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |

Output: `removed` boolean — Removed by this run · `alreadyGone` boolean — They had no manager · `user` string — User.

```json
{"type":"graph.removeManager","version":1,"label":"Remove manager","config":{"connection":"<connection id>","user":"<user>"}}
```

## `graph.replyToMessage@1` — Microsoft Graph — Reply to message

Replies to an email in a Microsoft 365 mailbox, to its sender or everyone on it.

Ports: `out` · writes

Needs, on its connection: `Mail.Send`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `mailbox` | text, required | Mailbox: The mailbox this node works in — a user's sign-in address, or their object id. Your app needs Mail.Send for it. |
| `messageId` | text, required | Message id: The id a list of messages answered, as Graph gave it. |
| `comment` | text, required | Reply: The text of the reply. Graph quotes the original message below it. |
| `replyAll` | true/false, default `false` | Reply to all: Off replies to the sender alone, or to the message's reply-to addresses. |

Output: `sent` boolean — Sent · `message` string — Replied-to message id · `replyAll` boolean — Replied to all.

```json
{"type":"graph.replyToMessage","version":1,"label":"Reply to message","config":{"replyAll":false,"connection":"<connection id>","mailbox":"<mailbox>","messageId":"<message id>","comment":"<reply>"}}
```

## `graph.request@1` — Microsoft Graph — Custom request

Calls any Microsoft Graph endpoint using a saved connection.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Needs the application permission the endpoint's own Microsoft Graph reference names, granted with admin consent in Microsoft Entra ID. — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `version` | "v1.0" or "beta", default `"v1.0"` | API version |
| `method` | "GET" or "POST" or "PUT" or "PATCH" or "DELETE", default `"GET"` | Method |
| `path` | text, required | Path: Everything after the version — e.g. users, me/messages, or users/{{ json.id }}/events. Each step is encoded for you; query parameters belong below. |
| `query` | object of text, default `{}` | Query parameters: OData parameters like $select, $filter, $top — encoded for you. |
| `headers` | object of text, default `{}` | Headers: Extra request headers, e.g. ConsistencyLevel: eventual or Prefer: outlook.timezone="Europe/Amsterdam". Credentials never belong here — the connection carries them. |
| `body` | any JSON, optional | Body: JSON text or an object — an object is sent as application/json. — used when `method` is not "GET" |
| `pagination` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link on a GET, re-sending your headers on every page, and refuses past Stop after rather than truncating. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. — used when `pagination` is true |
| `acceptStatuses` | text, default `""` | Treat these statuses as data: Comma-separated status codes, e.g. "404, 409". They come back in status instead of failing the node. |

Output: `status` number — Status code · `body` any — Response body · `headers` object — Response headers · `pages` number — Pages fetched (only with Fetch every page on).

```json
{"type":"graph.request","version":1,"label":"Custom request","config":{"version":"v1.0","method":"GET","query":{},"headers":{},"pagination":false,"maxPages":10,"acceptStatuses":"","connection":"<connection id>","path":"<path>"}}
```

## `graph.restoreUser@1` — Microsoft Graph — Restore user

Restores a deleted person to your Microsoft 365 directory, with mailbox and groups.

Ports: `out` · writes

Needs, on its connection: `User.DeleteRestore.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `userId` | text, required | User id: The deleted user's object id — a deleted user has no sign-in address to use. |
| `newUserPrincipalName` | text, default `""` | New sign-in address: Leave empty to keep the old one. Use it when someone else took it meanwhile. |
| `autoReconcileProxyConflict` | true/false, default `false` | Drop conflicting mail addresses: Removes the user's mail addresses that an active user now holds. |

Output: `id` string — User id · `user` object — User · `user.displayName` string — Display name.

```json
{"type":"graph.restoreUser","version":1,"label":"Restore user","config":{"newUserPrincipalName":"","autoReconcileProxyConflict":false,"connection":"<connection id>","userId":"<user id>"}}
```

## `graph.revokeSignInSessions@1` — Microsoft Graph — Revoke sign-in sessions

Signs a person out of every Microsoft 365 app and browser they use.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `User.RevokeSessions.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. Guests sign in at home, so they stay signed in. |

Output: `revoked` boolean — Signed out · `user` string — User.

```json
{"type":"graph.revokeSignInSessions","version":1,"label":"Revoke sign-in sessions","config":{"connection":"<connection id>","user":"<user>"}}
```

## `graph.searchFiles@1` — Microsoft Graph — Search files

Finds files and folders in OneDrive or SharePoint that match a text.

Ports: `out` · reads

Needs, on its connection: `Files.Read.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `driveOf` | "user" or "group" or "site" or "drive", default `"user"` | Drive of |
| `owner` | text, required | Owner: The person's id or sign-in address, the group's id, the site's id or the drive's id — whichever Drive of names. |
| `query` | text, required | Search for: Matched against names, contents and other fields across the whole drive, as Microsoft 365's own search does. |
| `select` | text, default `""` | Fields: Comma-separated property names. Empty answers Microsoft 365's default set. |
| `top` | number, default `100` | Items per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Graph's own next-page link. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 50. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Files and folders found · `count` number — Items in this answer · `hasMore` boolean — More items are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"graph.searchFiles","version":1,"label":"Search files","config":{"driveOf":"user","select":"","top":100,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>","owner":"<owner>","query":"<search for>"}}
```

## `graph.sendMail@1` — Microsoft Graph — Send email

Sends an email from a Microsoft 365 mailbox, with files attached.

Ports: `out` · writes

Needs, on its connection: `Mail.Send`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `mailbox` | text, required | Send from: The mailbox this mail is sent FROM — a user's sign-in address, or their object id. Your app needs Mail.Send for it. |
| `to` | text, required | To |
| `cc` | text, default `""` | Cc |
| `bcc` | text, default `""` | Bcc |
| `replyTo` | text, default `""` | Reply to |
| `subject` | text, required | Subject |
| `bodyFormat` | "text" or "html", default `"text"` | Body format |
| `body` | text, required | Body |
| `attachments` | object of text, default `{}` | Attachments: File name → base64 contents — use {{ file("report.pdf").base64 }} as the value. Up to 3 MB of attachments in total; larger files need an upload session, which this node does not do. |
| `saveToSentItems` | true/false, default `true` | Save in Sent items |

Output: `accepted` boolean — Accepted · `recipients` array — Recipients · `requestId` string — Graph request-id (from the response header).

```json
{"type":"graph.sendMail","version":1,"label":"Send email","config":{"cc":"","bcc":"","replyTo":"","bodyFormat":"text","attachments":{},"saveToSentItems":true,"connection":"<connection id>","mailbox":"<send from>","to":"<to>","subject":"<subject>","body":"<body>"}}
```

## `graph.setManager@1` — Microsoft Graph — Set manager

Changes who a person reports to in your Microsoft 365 directory.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `User.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `managerId` | text, required | Manager id: The manager's object id. It replaces any manager the user has. |

Output: `updated` boolean — Updated · `user` string — User · `manager` string — Manager id.

```json
{"type":"graph.setManager","version":1,"label":"Set manager","config":{"connection":"<connection id>","user":"<user>","managerId":"<manager id>"}}
```

## `graph.setUserPhoto@1` — Microsoft Graph — Set user photo

Replaces a person's Microsoft 365 profile picture with a JPEG image.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `ProfilePhoto.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `photoBase64` | text, required | Photo: Base64 of a JPEG — {{ file("photo.jpg").base64 }}. Microsoft Graph takes a photo of at most 4 MB. — its `{{ }}` must give a string |

Output: `updated` boolean — Photo replaced · `user` string — User.

```json
{"type":"graph.setUserPhoto","version":1,"label":"Set user photo","config":{"connection":"<connection id>","user":"<user>","photoBase64":"<photo>"}}
```

## `graph.updateEvent@1` — Microsoft Graph — Update calendar event

Changes the subject, time, place or guests of a meeting in a Microsoft 365 calendar.

Ports: `out` · writes

Needs, on its connection: `Calendars.ReadWrite`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | Calendar of: A user's object id, or their sign-in address. |
| `calendarId` | text, default `""` | Calendar id: Leave empty for the user's default calendar. |
| `eventId` | text, required | Event id: The id a Create or List calendar events node answered. |
| `subject` | text, default `""` | Subject: Leave empty to keep the subject. |
| `bodyFormat` | "text" or "html", default `"text"` | Body format |
| `body` | text, default `""` | Body: Leave empty to keep the body. A new body replaces the old one: on an online meeting, carry its join details over, or it stops being online. |
| `start` | text, default `""` | Starts: ISO 8601 without a zone — the time zone below applies. Empty keeps it. |
| `end` | text, default `""` | Ends: Empty keeps it. |
| `timeZone` | text, default `"UTC"` | Time zone: The zone of Starts and Ends, an IANA name such as Europe/Amsterdam. |
| `location` | text, default `""` | Location: Leave empty to keep the location. |
| `attendees` | text, default `""` | Invite: Leave empty to keep the guests. When filled in, this REPLACES the whole list: anyone left out is uninvited. |
| `attendeeType` | "required" or "optional", default `"required"` | Invitation type |
| `isOnlineMeeting` | "" or "true" or "false", default `""` | Online meeting |

Output: `id` string — Event id · `webLink` string — Link in Outlook · `event` object — Event.

```json
{"type":"graph.updateEvent","version":1,"label":"Update calendar event","config":{"calendarId":"","subject":"","bodyFormat":"text","body":"","start":"","end":"","timeZone":"UTC","location":"","attendees":"","attendeeType":"required","isOnlineMeeting":"","connection":"<connection id>","user":"<calendar of>","eventId":"<event id>"}}
```

## `graph.updateGroup@1` — Microsoft Graph — Update group

Changes the name, alias, description or visibility of a directory group.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `Group.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `groupId` | text, required | Group id |
| `displayName` | text, default `""` | Name: Blank leaves it as it is. |
| `description` | text, default `""` | Description: Blank leaves it as it is. |
| `mailNickname` | text, default `""` | Mail alias: Blank leaves it as it is. At most 64 ASCII characters, without spaces or @ ( ) \ [ ] " ; : . < > ,. |
| `visibility` | "" or "Private" or "Public", default `""` | Visibility: Microsoft 365 groups only. |

Output: `updated` boolean — Updated · `group` string — Group id · `changed` array — Properties changed.

```json
{"type":"graph.updateGroup","version":1,"label":"Update group","config":{"displayName":"","description":"","mailNickname":"","visibility":"","connection":"<connection id>","groupId":"<group id>"}}
```

## `graph.updateListItem@1` — Microsoft Graph — Update SharePoint list item

Changes column values of one row in a SharePoint list.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `Sites.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `siteId` | text, required | Site id: The id Get SharePoint site answered — host name, site GUID and web GUID. |
| `listId` | text, required | List: The list's id, or its title. |
| `itemId` | text, required | Item id |
| `fields` | any JSON, optional | Column values: Only the columns to change, internal name to value, as JSON — or one {{ }} expression that produces the object. Columns left out keep their value. |

Output: `item` string — Item id · `fields` object — Column values.

```json
{"type":"graph.updateListItem","version":1,"label":"Update SharePoint list item","config":{"connection":"<connection id>","siteId":"<site id>","listId":"<list>","itemId":"<item id>"}}
```

## `graph.updateMessage@1` — Microsoft Graph — Update message

Changes an email's read state, importance, categories or flag in Microsoft 365.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `Mail.ReadWrite`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `mailbox` | text, required | Mailbox: The mailbox this node works in — a user's sign-in address, or their object id. Your app needs Mail.ReadWrite for it. |
| `messageId` | text, required | Message id: The id a list of messages answered, as Graph gave it. |
| `isRead` | "" or "true" or "false", default `""` | Read |
| `importance` | "" or "low" or "normal" or "high", default `""` | Importance |
| `categories` | text, default `""` | Categories: Comma-separated category names. They replace the message's categories; leave empty to keep them. |
| `flag` | "" or "notFlagged" or "flagged" or "complete", default `""` | Flag |

Output: `message` object — Message · `message.id` string — Id · `message.subject` string — Subject.

```json
{"type":"graph.updateMessage","version":1,"label":"Update message","config":{"isRead":"","importance":"","categories":"","flag":"","connection":"<connection id>","mailbox":"<mailbox>","messageId":"<message id>"}}
```

## `graph.updatePlannerTask@1` — Microsoft Graph — Update Planner task

Changes a Planner task's title, column, progress, priority or dates.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `Tasks.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `taskId` | text, required | Task id |
| `etag` | text, default `""` | ETag: Leave empty to change the task as it is now. Give the ETag a read answered to refuse the change when someone changed the task since. |
| `title` | text, default `""` | Title: Blank leaves it as it is. |
| `bucketId` | text, default `""` | Bucket id: Moves the task to that column of its plan. Blank leaves it where it is. |
| `progress` | "" or "notStarted" or "inProgress" or "completed", default `""` | Progress |
| `priority` | "" or "urgent" or "important" or "medium" or "low", default `""` | Priority |
| `startDateTime` | text, default `""` | Starts: ISO 8601 in UTC. Blank leaves it as it is. |
| `dueDateTime` | text, default `""` | Due |

Output: `id` string — Task id · `etag` string — The task's new ETag · `changed` array — Properties changed · `task` object — Task.

```json
{"type":"graph.updatePlannerTask","version":1,"label":"Update Planner task","config":{"etag":"","title":"","bucketId":"","progress":"","priority":"","startDateTime":"","dueDateTime":"","connection":"<connection id>","taskId":"<task id>"}}
```

## `graph.updateUser@1` — Microsoft Graph — Update user

Changes a person's job title, department or other profile details in Microsoft 365.

Ports: `out` · writes (safe to repeat)

Needs, on its connection: `User.ReadUpdate.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `user` | text, required | User: A user's object id, or their sign-in address. |
| `displayName` | text, default `""` | Display name: Leave a field blank to keep its value. A blank cannot clear one. |
| `givenName` | text, default `""` | First name |
| `surname` | text, default `""` | Last name |
| `jobTitle` | text, default `""` | Job title |
| `department` | text, default `""` | Department |
| `officeLocation` | text, default `""` | Office |
| `mobilePhone` | text, default `""` | Mobile phone: Needs the application permission User-Phone.ReadWrite.All as well. |
| `usageLocation` | text, default `""` | Usage location: A two-letter country code, e.g. NL. A licence can only be assigned once it is set. |
| `accountEnabled` | "" or "true" or "false", default `""` | Can sign in: Needs User.EnableDisableAccount.All as well, and an admin role when the user is an admin. |

Output: `updated` boolean — Updated · `user` string — User · `changed` array — Properties changed.

```json
{"type":"graph.updateUser","version":1,"label":"Update user","config":{"displayName":"","givenName":"","surname":"","jobTitle":"","department":"","officeLocation":"","mobilePhone":"","usageLocation":"","accountEnabled":"","connection":"<connection id>","user":"<user>"}}
```

## `graph.uploadFile@1` — Microsoft Graph — Upload file

Stores a file in a OneDrive or SharePoint folder.

Ports: `out` · writes

Needs, on its connection: `Files.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `driveOf` | "user" or "group" or "site" or "drive", default `"user"` | Drive of |
| `owner` | text, required | Owner: The person's id or sign-in address, the group's id, the site's id or the drive's id — whichever Drive of names. |
| `folder` | text, default `""` | Folder: The folder's id, or its path from the drive's root starting with /. Leave empty for the root. |
| `name` | text, required | File name: The file's name, extension included. Pick the file, then paste its base64 below. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("name").base64 }}. One call carries it whole, so the run's parse limit bounds it; Upload large file sends a bigger Storage file in parts. — its `{{ }}` must give a string |
| `contentType` | text, default `"application/octet-stream"` | Media type |
| `conflictBehavior` | "replace" or "rename" or "fail", default `"replace"` | If the name is taken |

Output: `id` string — File id · `webUrl` string — Link in Microsoft 365 · `item` object — File.

```json
{"type":"graph.uploadFile","version":1,"label":"Upload file","config":{"driveOf":"user","folder":"","contentType":"application/octet-stream","conflictBehavior":"replace","connection":"<connection id>","owner":"<owner>","name":"<file name>","contentBase64":"<file contents>"}}
```

## `graph.uploadLargeFile@1` — Microsoft Graph — Upload large file

Sends a Storage file to a OneDrive or SharePoint folder in parts.

Ports: `out` · writes

Needs, on its connection: `Files.ReadWrite.All`

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `microsoft-graph`) |
| `driveOf` | "user" or "group" or "site" or "drive", default `"user"` | Drive of |
| `owner` | text, required | Owner: The person's id or sign-in address, the group's id, the site's id or the drive's id — whichever Drive of names. |
| `folder` | text, default `""` | Folder: The folder's id, or its path from the drive's root starting with /. Leave empty for the root. |
| `file` | text, required | Storage file: A file's name is the whole key — exports/report.csv is a name, not a folder. It is sent in 5 MB parts, so the run's file bound limits it — Storage holds up to 100 MB per file — not Microsoft Graph's. — a file name, listed in `requires.files` |
| `name` | text, default `""` | File name: The name in the folder. Leave empty for the Storage name after its last /. |
| `conflictBehavior` | "replace" or "rename" or "fail", default `"replace"` | If the name is taken |

Output: `id` string — File id · `webUrl` string — Link in Microsoft 365 · `item` object — File.

```json
{"type":"graph.uploadLargeFile","version":1,"label":"Upload large file","config":{"driveOf":"user","folder":"","name":"","conflictBehavior":"replace","connection":"<connection id>","owner":"<owner>","file":"<storage file>"}}
```


---

## File: references/nodes/hibob.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# HiBob nodes (`hibob.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `hibob.cancelTimeOffRequest@1` — HiBob — Cancel time off request

Cancels a HiBob time off request.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `requestId` | text, required | Request id: The id the Request time off node answered with, or the one in Bob's own time off screen. |

Output: `requestId` string — Request id · `cancelled` boolean — This run cancelled it.

```json
{"type":"hibob.cancelTimeOffRequest","version":1,"label":"Cancel time off request","config":{"connection":"<connection id>","employeeId":"<employee id>","requestId":"<request id>"}}
```

## `hibob.completeTask@1` — HiBob — Complete task

Marks a HiBob task as done.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `taskId` | text, required | Task id: The id the List tasks node answers for each task. |

Output: `taskId` string — Task id · `completed` boolean — The task is done.

```json
{"type":"hibob.completeTask","version":1,"label":"Complete task","config":{"connection":"<connection id>","taskId":"<task id>"}}
```

## `hibob.createEmployee@1` — HiBob — Create employee

Creates a HiBob employee with their first work entry.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `firstName` | text, required | First name |
| `surname` | text, required | Surname |
| `email` | text, required | Work e-mail: Bob requires this to be unique in the company — it is the person's login. |
| `site` | text, required | Site: Must be a value from Bob's site list — the display value, not the id. The Read company list node with site shows them. |
| `startDate` | text, required | Start date: YYYY-MM-DD. |
| `department` | text, default `""` | Department |
| `title` | text, default `""` | Job title |
| `manager` | text, default `""` | Manager: The manager's employee id, as Bob's own work.manager field holds it. |
| `extraFields` | object (any keys), default `{}` | Other Bob fields: Any other Bob field, in the same nested shape a search answer uses — for example {"home":{"mobilePhone":"0612345678"}} or {"userData":{"custom":{"field_17257492283795":"…"}}}. The List employee fields node gives you every id. |

Output: `employeeId` string — Employee id · `employee` object — Employee.

```json
{"type":"hibob.createEmployee","version":1,"label":"Create employee","config":{"department":"","title":"","manager":"","extraFields":{},"connection":"<connection id>","firstName":"<first name>","surname":"<surname>","email":"<work e-mail>","site":"<site>","startDate":"<start date>"}}
```

## `hibob.createEmployeeTableEntry@1` — HiBob — Add employee table entry

Adds a work, salary or other table entry to a HiBob employee.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `table` | "work" or "employment" or "salaries" or "equities" or "variable" or "training" or "bank-accounts", default `"work"` | Table: Lifecycle status is Bob's own derived table and takes no new entry — terminate the employee instead. |
| `entry` | object (any keys), default `{}` | Entry: The entry's fields, as Bob's own answer for this table spells them. effectiveDate (YYYY-MM-DD) is required on every historical table. Read the same table with the List employee table node first to see the names your Bob uses. |

Output: `entry` object — Entry · `entryId` string — Entry id.

```json
{"type":"hibob.createEmployeeTableEntry","version":1,"label":"Add employee table entry","config":{"table":"work","entry":{},"connection":"<connection id>","employeeId":"<employee id>"}}
```

## `hibob.createTimeOffRequest@1` — HiBob — Request time off

Submits a HiBob time off request in the shape the policy wants.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `policyType` | text, required | Policy type: As the List time off policy types node spells it. |
| `requestRangeType` | "days" or "hours" or "portionOnRange" or "hoursOnRange" or "specificHoursOnRange" or "differentDayDurations" or "differentSpecificHoursDayDurations", default `"days"` | How the time is counted: Bob's policies are counted in days or in hours, and the request has to be written the way the policy counts. The List time off policy types node says which. |
| `startDate` | text, required | From: YYYY-MM-DD. |
| `endDate` | text, required | To: YYYY-MM-DD. The same day as From for a single day. |
| `startDatePortion` | "all_day" or "afternoon", default `"all_day"` | First day — used when `requestRangeType` is "days" |
| `endDatePortion` | "all_day" or "morning", default `"all_day"` | Last day — used when `requestRangeType` is "days" |
| `hours` | number, default `0` | Hours — used when `requestRangeType` is "hours" |
| `minutes` | number, default `0` | Minutes — used when `requestRangeType` is "hours" |
| `dailyHours` | number, default `0` | Hours per day — used when `requestRangeType` is "hoursOnRange" |
| `dailyMinutes` | number, default `0` | Minutes per day — used when `requestRangeType` is "hoursOnRange" |
| `dayPortion` | "morning" or "afternoon", default `"morning"` | Which half of the day — used when `requestRangeType` is "portionOnRange" |
| `localStartTime` | text, default `""` | From time: HH:mm in the employee's own time zone. — used when `requestRangeType` is "specificHoursOnRange" |
| `localEndTime` | text, default `""` | To time — used when `requestRangeType` is "specificHoursOnRange" |
| `durations` | list of any JSON, default `[]` | Per-day durations: One entry per day between From and To, in Bob's own shape: a portion for each day, or a start and end time for each day. — used when `requestRangeType` is one of "differentDayDurations", "differentSpecificHoursDayDurations" |
| `description` | text, default `""` | Note: What the person wrote on the request. Everyone who may see the request reads it. |
| `reasonCode` | number, default `0` | Reason: The numeric id of a reason Bob offers for this policy — the List time off policy types node leads to them. 0 means no reason. |
| `approver` | text, default `""` | Approver: The employee id of the person who should approve it, instead of the default. |
| `skipManagerApproval` | true/false, default `false` | Approve it straight away: Admins only. Switching this on creates the request already approved and skips the person's manager entirely. |

Output: `requestId` string — Request id · `request` object — Request.

```json
{"type":"hibob.createTimeOffRequest","version":1,"label":"Request time off","config":{"requestRangeType":"days","startDatePortion":"all_day","endDatePortion":"all_day","hours":0,"minutes":0,"dailyHours":0,"dailyMinutes":0,"dayPortion":"morning","localStartTime":"","localEndTime":"","durations":[],"description":"","reasonCode":0,"approver":"","skipManagerApproval":false,"connection":"<connection id>","employeeId":"<employee id>","policyType":"<policy type>","startDate":"<from>","endDate":"<to>"}}
```

## `hibob.downloadReport@1` — HiBob — Download report

Downloads a HiBob report as rows or as a file.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `reportId` | text, required | Report id: The List reports node gives you the id. |
| `format` | "csv" or "xlsx" or "json", default `"json"` | Format |
| `includeInfo` | true/false, default `false` | Add the report's own header rows: Adds the report's name and the date it ran as extra rows at the top of the file — handy for a human, in the way of a parser. |
| `locale` | text, default `""` | Language: A locale like en-GB or nl-NL. Leave empty for the company's own. |
| `humanReadable` | "" or "APPEND" or "REPLACE", default `""` | List values: Bob only applies this to the rows form. — used when `format` is "json" |

Output: `rows` array — Rows · `rowCount` number — Rows in this answer · `file` any — The report as a file · `file.name` string — File name · `file.contentType` string — Content type · `file.base64` string — File contents (base64) · `file.size` number — Size in bytes.

```json
{"type":"hibob.downloadReport","version":1,"label":"Download report","config":{"format":"json","includeInfo":false,"locale":"","humanReadable":"","connection":"<connection id>","reportId":"<report id>"}}
```

## `hibob.getEmployee@1` — HiBob — Get employee

Reads one HiBob employee by their id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id: The id in Bob's address bar when you open the person — app.hibob.com/employee-profile/333288388404568151 means 333288388404568151. The work e-mail is accepted here too. — a value from the vendor's own list |
| `fields` | text, default `""` | Fields to fetch: Leave empty for Bob's default set. The List employee fields node gives you every id — root.email, work.title, work.department, work.startDate and the rest. |
| `humanReadable` | "" or "APPEND" or "REPLACE", default `""` | List values: Bob stores list values as numeric ids; this turns them into their names. |

Output: `employee` object — Employee · `employeeId` string — Employee id.

```json
{"type":"hibob.getEmployee","version":1,"label":"Get employee","config":{"fields":"","humanReadable":"","connection":"<connection id>","employeeId":"<employee id>"}}
```

## `hibob.getTimeOffBalance@1` — HiBob — Get time off balance

Reads a HiBob employee's leave balance on a date.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `policyType` | text, required | Policy type: The name Bob gives the leave type, exactly as the List time off policy types node spells it. |
| `date` | text, required | On date: YYYY-MM-DD. The balance is the one that holds on that day, accruals to date included. |

Output: `balance` object — Balance, as Bob answered it · `totalBalanceAsOfDate` number — Balance on that date · `startingBalance` number — Starting balance · `totalTaken` number — Taken · `totalAdminAdjustments` number — Adjusted by an admin · `totalSystemAdjustments` number — Adjusted by Bob · `annualAllowance` number — Annual allowance · `currentAssignment` string — Assigned or Unassigned — Unassigned means the person was not on this policy.

```json
{"type":"hibob.getTimeOffBalance","version":1,"label":"Get time off balance","config":{"connection":"<connection id>","employeeId":"<employee id>","policyType":"<policy type>","date":"<on date>"}}
```

## `hibob.inviteEmployee@1` — HiBob — Invite employee

Sends a HiBob employee their welcome invitation.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `wizardId` | text, default `""` | Welcome wizard id: Which onboarding wizard the person sees. In Bob: Settings → Onboarding → Welcome wizards — the id is in the address bar. Leave empty for the company default. |

Output: `employeeId` string — Employee id · `invited` boolean — Bob sent the invitation.

```json
{"type":"hibob.inviteEmployee","version":1,"label":"Invite employee","config":{"wizardId":"","connection":"<connection id>","employeeId":"<employee id>"}}
```

## `hibob.listBulkTableEntries@1` — HiBob — List table for many employees

Reads a Bob table for up to 200 employees at a time.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `table` | "work" or "employment" or "salaries" or "lifecycle" or "deduction" or "dependents" or "entitlement" or "equities" or "right-to-work" or "variable", default `"work"` | Table: Salary history, Equity grants, Variable pay and Deductions are sensitive: the service user's group needs View and Edit on the fields, and View history on the category. |
| `employeeIds` | text, default `""` | Employees: Leave empty for everyone Bob lets this service user see. Bob takes at most 200 ids in one request; anyone asked for and not answered comes back under Errors. |
| `includeArchived` | true/false, default `false` | Include archived entries |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Bob's cursor, and refuses past 20,000 entries rather than answering a short list. |
| `limit` | number, default `50` | Entries per call: 1 to 200. Bob's own default is 50. |
| `cursor` | text, default `""` | Start at cursor: The Next cursor from an earlier run of this node, to carry on where it stopped. Bob's cursors expire — leave this empty to start at the beginning. |

Output: `results` array — Entries · `entryCount` number — Entries in this answer · `errors` array — Employees Bob could not answer for · `hasMore` boolean — More entries are waiting behind this page · `nextCursor` string — Next cursor.

```json
{"type":"hibob.listBulkTableEntries","version":1,"label":"List table for many employees","config":{"table":"work","employeeIds":"","includeArchived":false,"returnAll":false,"limit":50,"cursor":"","connection":"<connection id>"}}
```

## `hibob.listCustomTableEntries@1` — HiBob — List custom table

Reads a HiBob custom table's entries for one employee.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `tableId` | text, required | Custom table id: The List custom tables node gives you the id and the columns it holds. |

Output: `entries` array — Entries · `entryCount` number — Entries in this answer.

```json
{"type":"hibob.listCustomTableEntries","version":1,"label":"List custom table","config":{"connection":"<connection id>","employeeId":"<employee id>","tableId":"<custom table id>"}}
```

## `hibob.listCustomTableMetadata@1` — HiBob — List custom tables

Lists HiBob's custom tables with their columns and types.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: A column whose type is a list carries typeData.listId — feed that to the Read company list node to turn its ids into names. — a connection id from `requires.connections` (provider `hibob`) |

Output: `tables` array — Custom tables · `tableCount` number — Custom tables in this answer.

```json
{"type":"hibob.listCustomTableMetadata","version":1,"label":"List custom tables","config":{"connection":"<connection id>"}}
```

## `hibob.listEmployeeDocuments@1` — HiBob — List employee documents

Lists a HiBob employee's documents and their download links.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id: Bob only lists the folders this service user's permission group may see — a confidential folder it may not read is simply absent. — a value from the vendor's own list |

Output: `documents` array — Documents · `documentCount` number — Documents in this answer.

```json
{"type":"hibob.listEmployeeDocuments","version":1,"label":"List employee documents","config":{"connection":"<connection id>","employeeId":"<employee id>"}}
```

## `hibob.listEmployeeFields@1` — HiBob — List employee fields

Lists every HiBob employee field, with the id to use in other nodes.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: This list needs no permissions, so it shows fields the service user may not actually be allowed to read. — a connection id from `requires.connections` (provider `hibob`) |

Output: `fields` array — Fields · `fieldCount` number — Fields in this answer.

```json
{"type":"hibob.listEmployeeFields","version":1,"label":"List employee fields","config":{"connection":"<connection id>"}}
```

## `hibob.listEmployeeTableEntries@1` — HiBob — List employee table

Reads one employee's work, salary, employment or other Bob table.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `table` | "work" or "employment" or "salaries" or "equities" or "variable" or "training" or "bank-accounts" or "lifecycle", default `"work"` | Table: Salary history, Equity grants, Variable pay and Bank accounts are sensitive: the service user's group needs View and Edit on the fields, and View history on the category, or Bob leaves the columns out without saying so. |
| `includeArchived` | true/false, default `false` | Include archived entries |

Output: `entries` array — Entries · `entryCount` number — Entries in this answer · `restrictedColumns` object — Columns Bob left out because this service user may not see them.

```json
{"type":"hibob.listEmployeeTableEntries","version":1,"label":"List employee table","config":{"table":"work","includeArchived":false,"connection":"<connection id>","employeeId":"<employee id>"}}
```

## `hibob.listReports@1` — HiBob — List reports

Lists the HiBob reports this service user may download.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Only the reports whose categories this service user may see. — a connection id from `requires.connections` (provider `hibob`) |

Output: `reports` array — Reports · `reportCount` number — Reports in this answer.

```json
{"type":"hibob.listReports","version":1,"label":"List reports","config":{"connection":"<connection id>"}}
```

## `hibob.listTasks@1` — HiBob — List tasks

Lists open HiBob onboarding and workflow tasks.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, default `""` | Employee id: Leave empty for every open task in the company. Pick someone for their own tasks. — a value from the vendor's own list |
| `taskStatus` | "open" or "closed" or "", default `"open"` | Status: Bob answers only open tasks when no employee is named. — used when `employeeId` is not "" |

Output: `tasks` array — Tasks · `taskCount` number — Tasks in this answer.

```json
{"type":"hibob.listTasks","version":1,"label":"List tasks","config":{"employeeId":"","taskStatus":"open","connection":"<connection id>"}}
```

## `hibob.listTimeOffChanges@1` — HiBob — List time off changes

Lists HiBob time off requests that changed since a date.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `since` | text, required | Changed since: YYYY-MM-DD, or a full date and time. Bob keeps six months of changes — asking for more is refused here rather than answered short. |
| `to` | text, default `""` | Up to: Leave empty for right now. |
| `includePending` | true/false, default `false` | Include requests still awaiting approval |

Output: `changes` array — Changed requests · `changeCount` number — Changed requests in this answer.

```json
{"type":"hibob.listTimeOffChanges","version":1,"label":"List time off changes","config":{"to":"","includePending":false,"connection":"<connection id>","since":"<changed since>"}}
```

## `hibob.listTimeOffPolicyTypes@1` — HiBob — List time off policy types

Lists HiBob's time off policy types and their names.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The names here are what the Get time off balance and Request time off nodes want in their Policy type field. — a connection id from `requires.connections` (provider `hibob`) |

Output: `policyTypes` array — Policy types · `policyTypeCount` number — Policy types in this answer.

```json
{"type":"hibob.listTimeOffPolicyTypes","version":1,"label":"List time off policy types","config":{"connection":"<connection id>"}}
```

## `hibob.listWhosOut@1` — HiBob — List who's out

Shows who is off work in HiBob between two dates.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `from` | text, required | From: YYYY-MM-DD, the first day of the period. |
| `to` | text, required | To: YYYY-MM-DD, the last day of the period. |
| `includeHourly` | true/false, default `true` | Include part-day absences |
| `includePrivate` | true/false, default `false` | Include private leave types: Bob hides policies an admin marked private. Switching this on needs the permission to see them, or Bob leaves them out anyway. |
| `includePending` | true/false, default `false` | Include requests still awaiting approval |
| `includeWorkingRequests` | true/false, default `false` | Count working from home as out: Bob counts Work From Home and business travel as time off by default. Leave this off if out means not working. |

Output: `outs` array — People who are out · `outCount` number — People who are out.

```json
{"type":"hibob.listWhosOut","version":1,"label":"List who's out","config":{"includeHourly":true,"includePrivate":false,"includePending":false,"includeWorkingRequests":false,"connection":"<connection id>","from":"<from>","to":"<to>"}}
```

## `hibob.readCompanyList@1` — HiBob — Read company list

Reads a HiBob company list, like sites, departments or titles.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `listName` | text, default `""` | List name: Leave empty to get every list Bob has. Pick one to get its items with their ids — the values Create employee and the table nodes want. — a value from the vendor's own list |
| `includeArchived` | true/false, default `false` | Include archived values: Off, Bob leaves out values an admin has retired. |

Output: `lists` array — Lists · `listCount` number — Lists in this answer · `list` object — The list you asked for · `items` array — Values in the list · `itemCount` number — Values in the list.

```json
{"type":"hibob.readCompanyList","version":1,"label":"Read company list","config":{"listName":"","includeArchived":false,"connection":"<connection id>"}}
```

## `hibob.searchApplications@1` — HiBob — Search applications

Searches applications in Bob's hiring module.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `fields` | text, default `"/application/id,/application/candidateId,/application/jobOpeningId,/application/status,/application/stageId,/application/createdAt"` | Fields to fetch: At least one id, at most 50 — it starts with the six Bob's reference suggests. Application field ids are written like /application/id and are listed on Bob's own hiring API reference; the Search candidates node's /candidate/jobOpeningsToApplicationIds gives you the ids to filter on. |
| `filters` | list of any JSON, default `[]` | Filters: Each filter is a field id, an operator (equals, notEqual, greaterThan or lessThan) and the values to match. Bob refuses a field it does not filter on — filter the rest downstream. |
| `includeArchived` | true/false, default `false` | Include archived applications |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Bob's cursor, and refuses past 20,000 applications rather than answering a short list. |
| `limit` | number, default `100` | Applications per call: 1 to 200. Bob's own default is 100. |
| `cursor` | text, default `""` | Start at cursor: The Next cursor from an earlier run. Leave empty to start at the beginning. |

Output: `applications` array — Applications · `applicationCount` number — Applications in this answer · `hasMore` boolean — More applications are waiting behind this page · `nextCursor` string — Next cursor.

```json
{"type":"hibob.searchApplications","version":1,"label":"Search applications","config":{"fields":"/application/id,/application/candidateId,/application/jobOpeningId,/application/status,/application/stageId,/application/createdAt","filters":[],"includeArchived":false,"returnAll":false,"limit":100,"cursor":"","connection":"<connection id>"}}
```

## `hibob.searchCandidates@1` — HiBob — Search candidates

Searches candidates in Bob's own hiring module.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `fields` | text, default `"/candidate/id,/candidate/firstName,/candidate/lastName,/candidate/email"` | Fields to fetch: Bob's hiring search has no default set — at least one id, at most 50. The useful ones are /candidate/id, /candidate/firstName, /candidate/lastName, /candidate/email, /candidate/phone, /candidate/city, /candidate/country, /candidate/employeeId, /candidate/jobOpeningsToApplicationIds, /candidate/sourceType and /candidate/modificationDate. /candidate/employeeId is the link to the employee record once the candidate is hired — ask for it if the flow carries on into Bob itself. |
| `filters` | list of any JSON, default `[]` | Filters: Bob filters on /candidate/id, /candidate/email, /candidate/firstName, /candidate/lastName and /candidate/modificationDate, with equals, notEqual, greaterThan or lessThan. Anything else is refused by Bob — filter the rest downstream. |
| `includeArchived` | true/false, default `false` | Include archived candidates |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Bob's cursor, and refuses past 20,000 candidates rather than answering a short list. |
| `limit` | number, default `100` | Candidates per call: 1 to 200. Bob's own default is 100. |
| `cursor` | text, default `""` | Start at cursor: The Next cursor from an earlier run, to carry on where it stopped. Leave empty to start at the beginning. |

Output: `candidates` array — Candidates · `candidateCount` number — Candidates in this answer · `hasMore` boolean — More candidates are waiting behind this page · `nextCursor` string — Next cursor.

```json
{"type":"hibob.searchCandidates","version":1,"label":"Search candidates","config":{"fields":"/candidate/id,/candidate/firstName,/candidate/lastName,/candidate/email","filters":[],"includeArchived":false,"returnAll":false,"limit":100,"cursor":"","connection":"<connection id>"}}
```

## `hibob.searchEmployees@1` — HiBob — Search employees

Finds HiBob employees and returns the fields you ask for.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `fields` | text, default `""` | Fields to fetch: Leave empty for Bob's default set (root, About, Employment and Work). The List employee fields node gives you every id; the common ones are root.id, root.email, root.firstName, root.surname, root.fullName, root.displayName, work.title, work.department, work.site, work.startDate, work.manager, work.reportsTo and work.isManager. Asking for fewer fields is how you keep a big company's answer inside this run's size limit — this endpoint returns every matching employee in one go and does not paginate. |
| `filterField` | "" or "root.id" or "root.email", default `""` | Filter on: Bob supports exactly one filter, on the employee id or the e-mail, with equals. Anything else is a 400 — filter downstream instead. |
| `filterValues` | text, default `""` | Values to match: One or more ids or e-mail addresses. Bob matches any of them. — used when `filterField` is not "" |
| `showInactive` | true/false, default `false` | Include people who have left: Off, Bob answers only employees who are currently active. |
| `humanReadable` | "" or "APPEND" or "REPLACE", default `""` | List values: Bob stores list values as numeric ids. Readable values turns 1644513820829 into Amsterdam. |

Output: `employees` array — Employees · `employeeCount` number — Employees in this answer.

```json
{"type":"hibob.searchEmployees","version":1,"label":"Search employees","config":{"fields":"","filterField":"","filterValues":"","showInactive":false,"humanReadable":"","connection":"<connection id>"}}
```

## `hibob.searchJobOpenings@1` — HiBob — Search job openings

Searches job openings in Bob's hiring module.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `fields` | text, default `"/jobOpening/id,/jobOpening/title,/jobOpening/status,/jobOpening/department,/jobOpening/siteId,/jobOpening/createdAt"` | Fields to fetch: At least one id, at most 50 — it starts with the six Bob's reference suggests. Job opening field ids are written like /jobOpening/id and are listed on Bob's own hiring API reference. |
| `filters` | list of any JSON, default `[]` | Filters: Each filter is a field id, an operator (equals, notEqual, greaterThan or lessThan) and the values to match. Bob refuses a field it does not filter on — filter the rest downstream. |
| `includeArchived` | true/false, default `false` | Include closed openings |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Bob's cursor, and refuses past 20,000 openings rather than answering a short list. |
| `limit` | number, default `100` | Openings per call: 1 to 200. Bob's own default is 100. |
| `cursor` | text, default `""` | Start at cursor: The Next cursor from an earlier run. Leave empty to start at the beginning. |

Output: `jobOpenings` array — Job openings · `jobOpeningCount` number — Job openings in this answer · `hasMore` boolean — More openings are waiting behind this page · `nextCursor` string — Next cursor.

```json
{"type":"hibob.searchJobOpenings","version":1,"label":"Search job openings","config":{"fields":"/jobOpening/id,/jobOpening/title,/jobOpening/status,/jobOpening/department,/jobOpening/siteId,/jobOpening/createdAt","filters":[],"includeArchived":false,"returnAll":false,"limit":100,"cursor":"","connection":"<connection id>"}}
```

## `hibob.setEmployeeStartDate@1` — HiBob — Set start date

Sets or changes a HiBob employee's start date.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `startDate` | text, required | Start date: YYYY-MM-DD. Moving it re-dates the person's onboarding tasks and their first work entry. |

Output: `employeeId` string — Employee id · `startDate` string — Start date.

```json
{"type":"hibob.setEmployeeStartDate","version":1,"label":"Set start date","config":{"connection":"<connection id>","employeeId":"<employee id>","startDate":"<start date>"}}
```

## `hibob.terminateEmployee@2` — HiBob — Terminate employee

Records a HiBob employee's departure.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `terminationDate` | text, required | Termination date: YYYY-MM-DD — the day the employment ends. Bob dates the whole leaver workflow from this. |
| `lastDayOfWork` | text, default `""` | Last working day: YYYY-MM-DD, when the person stops working before the employment ends — garden leave, holiday taken out. Leave empty when it is the termination date. |
| `terminationReason` | text, default `""` | Reason: An entry of your Bob's termination reason list. Leave empty to set none. — a value from the vendor's own list |
| `reasonType` | text, default `""` | Reason type: An entry of your Bob's reason type list — voluntary or not, as your company words it. Leave empty to set none. — a value from the vendor's own list |
| `noticeLength` | number, optional | Notice period: How long the notice period is, in the unit below. Leave empty to set none. |
| `noticeUnit` | "days" or "weeks" or "month" or "years", default `"weeks"` | Notice period unit |

Output: `employeeId` string — Employee id · `terminated` boolean — Bob accepted the termination.

```json
{"type":"hibob.terminateEmployee","version":2,"label":"Terminate employee","config":{"lastDayOfWork":"","terminationReason":"","reasonType":"","noticeUnit":"weeks","connection":"<connection id>","employeeId":"<employee id>","terminationDate":"<termination date>"}}
```

### Older version `hibob.terminateEmployee@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `terminationDate` | text, required | Last working day: YYYY-MM-DD. Bob dates the whole leaver workflow from this. |
| `extraFields` | object (any keys), default `{}` | Other termination fields: Reason, type and notice period, spelled the way Bob's own termination record spells them. Open a terminated employee's Lifecycle table with the List employee table node to see the names your Bob uses. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"extraFields":{},"connection":"<connection id>","employeeId":"<employee id>","terminationDate":"<last working day>"}
{"lastDayOfWork":"","terminationReason":"","reasonType":"","noticeUnit":"weeks","extraFields":{},"connection":"<connection id>","employeeId":"<employee id>","terminationDate":"<last working day>"}
```

## `hibob.updateEmployee@1` — HiBob — Update employee

Changes fields on a HiBob employee.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id: The id Bob shows in the address bar of the person's profile. — a value from the vendor's own list |
| `fields` | object (any keys), default `{}` | Fields to change: Use the JSON structure from a search answer to spell the field. Only what you send changes; everything else is left alone. Position fields and table entries cannot be changed here — use the employee table nodes. Custom fields live under userData.custom. |

Output: `employeeId` string — Employee id · `changed` boolean — Bob changed something.

```json
{"type":"hibob.updateEmployee","version":1,"label":"Update employee","config":{"fields":{},"connection":"<connection id>","employeeId":"<employee id>"}}
```

## `hibob.uploadEmployeeDocument@1` — HiBob — Upload employee document

Puts a document in a HiBob employee's folder.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hibob`) |
| `employeeId` | text, required | Employee id — a value from the vendor's own list |
| `folder` | "shared" or "confidential" or "custom", default `"shared"` | Folder |
| `folderId` | text, default `""` | Folder id: In Bob: Settings → Documents → your folder — the id is in the address bar. Bob's own folder-metadata endpoint arrives as a node later. — used when `folder` is "custom" |
| `source` | "bytes" or "url", default `"bytes"` | Where the file comes from: Bob can download the file itself from an address it can reach. Pick that when the file is already somewhere public and the flow never needs to hold it. |
| `documentName` | text, required | Document name: The name Bob stores it under, extension included. — a file name, listed in `requires.files` |
| `documentUrl` | text, default `""` | Web address: Bob fetches the file from here, so the address has to be one Bob can reach. — used when `source` is "url" |
| `contentBase64` | text, default `""` | File contents: Base64 of the file — {{ file("name").base64 }}. — its `{{ }}` must give a string; used when `source` is "bytes" |
| `tags` | text, default `""` | Tags: Bob accepts tags only on the address form — the upload form has no field for them. — used when `source` is "url" |

Output: `documentId` string — Document id · `document` object — Document, as Bob answered it.

```json
{"type":"hibob.uploadEmployeeDocument","version":1,"label":"Upload employee document","config":{"folder":"shared","folderId":"","source":"bytes","documentUrl":"","contentBase64":"","tags":"","connection":"<connection id>","employeeId":"<employee id>","documentName":"<document name>"}}
```


---

## File: references/nodes/hubspot.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# HubSpot nodes (`hubspot.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `hubspot.associateRecords@1` — HubSpot — Associate records

Links two HubSpot records, with or without a label.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `fromObject` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | From object |
| `fromCustomObjectType` | text, default `""` | From custom object type id: The object type id, e.g. 2-1234567. — used when `fromObject` is "custom" |
| `fromId` | text, required | From record id — its `{{ }}` must give a string |
| `toObject` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"companies"` | To object |
| `toCustomObjectType` | text, default `""` | To custom object type id: The object type id, e.g. 2-1234567. — used when `toObject` is "custom" |
| `toId` | text, required | To record id — its `{{ }}` must give a string |
| `useLabel` | true/false, default `false` | Give the link a label: Off, HubSpot uses its own default link for this pair. On, the link carries the association type below. |
| `associationCategory` | "HUBSPOT_DEFINED" or "USER_DEFINED", default `"HUBSPOT_DEFINED"` | Association category — used when `useLabel` is true |
| `associationTypeId` | number, default `0` | Association type id: Leave at 0 to use HubSpot's default for this pair. The id is directional: if the from object is a contact and the to object a company, the id that marks that company primary is 1. — used when `useLabel` is true |

Output: `fromObjectId` string — From record id · `toObjectId` string — To record id · `labels` array — Labels on the link.

```json
{"type":"hubspot.associateRecords","version":1,"label":"Associate records","config":{"fromObject":"contacts","fromCustomObjectType":"","toObject":"companies","toCustomObjectType":"","useLabel":false,"associationCategory":"HUBSPOT_DEFINED","associationTypeId":0,"connection":"<connection id>","fromId":"<from record id>","toId":"<to record id>"}}
```

## `hubspot.batchCreateRecords@1` — HubSpot — Create records in bulk

Creates up to a hundred HubSpot records in one call.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. The List properties node shows it. — used when `object` is "custom" |
| `records` | any JSON, optional | Records: A list of objects, at most 100. A row that already has a properties key is sent as it is; any other row is sent as that record's properties. — its `{{ }}` must give an array |
| `traceIds` | true/false, default `false` | Report failures per record: Gives every record a trace id so HubSpot answers which ones failed instead of failing the whole call. The successes are kept and the failures come back under Errors. |

Output: `records` array — Created records · `recordCount` number — Records created · `errors` array — Records HubSpot refused · `numErrors` number — How many it refused.

```json
{"type":"hubspot.batchCreateRecords","version":1,"label":"Create records in bulk","config":{"object":"contacts","customObjectType":"","traceIds":false,"connection":"<connection id>","records":"<records>"}}
```

## `hubspot.batchReadRecords@1` — HubSpot — Read records in bulk

Fetches up to a hundred HubSpot records by id or by a unique property.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. The List properties node shows it. — used when `object` is "custom" |
| `recordIds` | any JSON, optional | Record ids: A list of at most 100 ids, or of the key values when a key property is named below. — its `{{ }}` must give an array |
| `idProperty` | text, default `""` | Look up by property: Leave empty to read by HubSpot record id. Fill in email to read contacts by e-mail, or the internal name of any unique-identifier property. |
| `properties` | text, default `""` | Properties to fetch: Comma-separated. Associated records cannot be read here — use the List associations node for those. |
| `propertiesWithHistory` | text, default `""` | Properties with their history: Comma-separated. Each one comes back with its previous values too. |

Output: `records` array — Records · `recordCount` number — Records in this answer · `errors` array — Ids HubSpot could not read · `numErrors` number — How many it could not read.

```json
{"type":"hubspot.batchReadRecords","version":1,"label":"Read records in bulk","config":{"object":"contacts","customObjectType":"","idProperty":"","properties":"","propertiesWithHistory":"","connection":"<connection id>","recordIds":"<record ids>"}}
```

## `hubspot.batchUpdateRecords@1` — HubSpot — Update records in bulk

Changes properties on up to a hundred HubSpot records in one call.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. The List properties node shows it. — used when `object` is "custom" |
| `records` | any JSON, optional | Records: A list of at most 100 objects, each with an id and the properties to change. — its `{{ }}` must give an array |

Output: `records` array — Updated records · `recordCount` number — Records updated · `errors` array — Records HubSpot refused · `numErrors` number — How many it refused.

```json
{"type":"hubspot.batchUpdateRecords","version":1,"label":"Update records in bulk","config":{"object":"contacts","customObjectType":"","connection":"<connection id>","records":"<records>"}}
```

## `hubspot.createNote@1` — HubSpot — Create note

Writes a note on a HubSpot record, with files attached.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `body` | text, required | Note: HubSpot stores a note's body as HTML. |
| `timestamp` | text, default `"{{ $macro.date.iso() }}"` | Time on the note: HubSpot requires a time on every note. The default is the moment it runs. — its `{{ }}` must give a string |
| `associateToObject` | "contacts" or "companies" or "deals" or "tickets", default `"contacts"` | Write it on |
| `associateToId` | text, required | Record id — its `{{ }}` must give a string |
| `attachmentFileIds` | text, default `""` | Attached file ids: File ids from the Upload file node. HubSpot separates several ids with a semicolon, and the value is sent exactly as you write it. |
| `ownerId` | text, default `""` | Owner id: The List owners node gives you the id. Use its id, never its userId. — a value from the vendor's own list |

Output: `noteId` string — Note id · `note` object — Note.

```json
{"type":"hubspot.createNote","version":1,"label":"Create note","config":{"timestamp":"{{ $macro.date.iso() }}","associateToObject":"contacts","attachmentFileIds":"","ownerId":"","connection":"<connection id>","body":"<note>","associateToId":"<record id>"}}
```

## `hubspot.createRecord@1` — HubSpot — Create record

Creates one HubSpot record and optionally links it to another.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. The List properties node shows it. — used when `object` is "custom" |
| `properties` | object (any keys), default `{}` | Properties: One row per property, keyed by its internal name. Deals need dealname, dealstage and pipeline; tickets need subject, hs_pipeline and hs_pipeline_stage; notes, tasks, calls and meetings need hs_timestamp. |
| `associateToObject` | "" or "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `""` | Link it to: Optional. HubSpot links the new record to an existing one in the same call. |
| `associateToId` | text, default `""` | Id of the record to link to — its `{{ }}` must give a string; used when `associateToObject` is not "" |
| `associationTypeId` | number, default `0` | Association type id: Leave at 0 to use HubSpot's default for this pair. The id is directional: starting at a contact, the id that marks a company primary is 1; starting at the company it is 2. |
| `associationCategory` | "HUBSPOT_DEFINED" or "USER_DEFINED", default `"HUBSPOT_DEFINED"` | Association category |

Output: `recordId` string — Record id · `record` object — Record · `properties` object — Properties.

```json
{"type":"hubspot.createRecord","version":1,"label":"Create record","config":{"object":"contacts","customObjectType":"","properties":{},"associateToObject":"","associateToId":"","associationTypeId":0,"associationCategory":"HUBSPOT_DEFINED","connection":"<connection id>"}}
```

## `hubspot.createTask@1` — HubSpot — Create task

Opens a task on a HubSpot record for someone to pick up.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `subject` | text, required | Title — its `{{ }}` must give a string |
| `body` | text, default `""` | Notes: HubSpot stores a task's notes as HTML. |
| `timestamp` | text, default `"{{ $macro.date.iso() }}"` | Due: HubSpot requires a due time on every task. The default is the moment it runs. — its `{{ }}` must give a string |
| `status` | "NOT_STARTED" or "IN_PROGRESS" or "WAITING" or "COMPLETED" or "DEFERRED", default `"NOT_STARTED"` | Status |
| `priority` | "NONE" or "LOW" or "MEDIUM" or "HIGH", default `"NONE"` | Priority |
| `associateToObject` | "contacts" or "companies" or "deals" or "tickets", default `"contacts"` | Put it on |
| `associateToId` | text, required | Record id — its `{{ }}` must give a string |
| `ownerId` | text, default `""` | Owner id: The List owners node gives you the id. Use its id, never its userId. — a value from the vendor's own list |

Output: `taskId` string — Task id · `task` object — Task.

```json
{"type":"hubspot.createTask","version":1,"label":"Create task","config":{"body":"","timestamp":"{{ $macro.date.iso() }}","status":"NOT_STARTED","priority":"NONE","associateToObject":"contacts","ownerId":"","connection":"<connection id>","subject":"<title>","associateToId":"<record id>"}}
```

## `hubspot.deleteRecord@1` — HubSpot — Delete record

Moves one HubSpot record to the recycling bin.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. The List properties node shows it. — used when `object` is "custom" |
| `recordId` | text, required | Record id — its `{{ }}` must give a string |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — It was gone already · `recordId` string — Record id.

```json
{"type":"hubspot.deleteRecord","version":1,"label":"Delete record","config":{"object":"contacts","customObjectType":"","connection":"<connection id>","recordId":"<record id>"}}
```

## `hubspot.dissociateRecords@1` — HubSpot — Remove association

Unlinks two HubSpot records, labels included.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `fromObject` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | From object |
| `fromCustomObjectType` | text, default `""` | From custom object type id: The object type id, e.g. 2-1234567. — used when `fromObject` is "custom" |
| `fromId` | text, required | From record id — its `{{ }}` must give a string |
| `toObject` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"companies"` | To object |
| `toCustomObjectType` | text, default `""` | To custom object type id: The object type id, e.g. 2-1234567. — used when `toObject` is "custom" |
| `toId` | text, required | To record id — its `{{ }}` must give a string |

Output: `dissociated` boolean — Unlinked by this run · `alreadyGone` boolean — There was no link left · `fromObjectId` string — From record id · `toObjectId` string — To record id.

```json
{"type":"hubspot.dissociateRecords","version":1,"label":"Remove association","config":{"fromObject":"contacts","fromCustomObjectType":"","toObject":"companies","toCustomObjectType":"","connection":"<connection id>","fromId":"<from record id>","toId":"<to record id>"}}
```

## `hubspot.getRecord@1` — HubSpot — Get record

Fetches one HubSpot record by its id or by a unique property.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. The List properties node shows it. — used when `object` is "custom" |
| `recordId` | text, required | Record id: The record's HubSpot id, or the value of the unique property named below. — its `{{ }}` must give a string |
| `idProperty` | text, default `""` | Look up by property: Leave empty to look the record up by its HubSpot record id. Fill in email to look a contact up by e-mail, or the internal name of any unique-identifier property. |
| `properties` | text, default `""` | Properties to fetch: Comma-separated. HubSpot leaves out anything you do not ask for. |
| `propertiesWithHistory` | text, default `""` | Properties with their history: Comma-separated. Each one comes back with its previous values too. |
| `associations` | text, default `""` | Associated objects: Comma-separated object names to return associated ids for. |
| `archived` | true/false, default `false` | Look in the recycling bin: Deleted records stay there for 90 days. |

Output: `record` object — Record · `recordId` string — Record id · `properties` object — Properties · `associations` object — Associated records.

```json
{"type":"hubspot.getRecord","version":1,"label":"Get record","config":{"object":"contacts","customObjectType":"","idProperty":"","properties":"","propertiesWithHistory":"","associations":"","archived":false,"connection":"<connection id>","recordId":"<record id>"}}
```

## `hubspot.listAssociations@1` — HubSpot — List associations

Lists the records one HubSpot record is linked to.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `fromObject` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | From object |
| `fromCustomObjectType` | text, default `""` | From custom object type id: The object type id, e.g. 2-1234567. — used when `fromObject` is "custom" |
| `fromId` | text, required | From record id — its `{{ }}` must give a string |
| `toObject` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"companies"` | Linked to object |
| `toCustomObjectType` | text, default `""` | Linked custom object type id: The object type id, e.g. 2-1234567. — used when `toObject` is "custom" |
| `limit` | number, default `100` | Links per call: 1 to 500. |
| `after` | text, default `""` | Start after: A paging cursor from an earlier run of this node. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows HubSpot's paging cursor, and refuses past 5,000 links rather than answering a short list. |

Output: `associations` array — Links · `associationCount` number — Links in this answer · `hasMore` boolean — More links are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"hubspot.listAssociations","version":1,"label":"List associations","config":{"fromObject":"contacts","fromCustomObjectType":"","toObject":"companies","toCustomObjectType":"","limit":100,"after":"","returnAll":false,"connection":"<connection id>","fromId":"<from record id>"}}
```

## `hubspot.listListMembers@1` — HubSpot — List list members

Lists the record ids in one HubSpot segment.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `listId` | text, required | List id: The List lists node gives you the id. — its `{{ }}` must give a string |
| `limit` | number, default `100` | Members per call: 1 to 500. |
| `after` | text, default `""` | Start after: A paging cursor from an earlier run of this node. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows HubSpot's paging cursor, and refuses past 5,000 members rather than answering a short list. |

Output: `recordIds` array — Record ids · `recordCount` number — Ids in this answer · `hasMore` boolean — More members are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"hubspot.listListMembers","version":1,"label":"List list members","config":{"limit":100,"after":"","returnAll":false,"connection":"<connection id>","listId":"<list id>"}}
```

## `hubspot.listLists@1` — HubSpot — List lists

Lists the segments a HubSpot account has.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `objectTypeId` | text, default `""` | Only lists of: An object type id — 0-1 contacts, 0-2 companies, 0-3 deals, 0-5 tickets. Leave empty for every list. |
| `processingTypes` | text, default `""` | Only lists of type: Comma-separated. Only manual and snapshot lists can have their membership changed by a flow. |
| `count` | number, default `100` | Lists per call: 1 to 500. HubSpot's own default is 20, so this is always sent. |
| `after` | text, default `""` | Start after: A paging cursor from an earlier run of this node. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows HubSpot's paging cursor, and refuses past 5,000 lists rather than answering a short list. |

Output: `lists` array — Lists · `listCount` number — Lists in this answer · `hasMore` boolean — More lists are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"hubspot.listLists","version":1,"label":"List lists","config":{"objectTypeId":"","processingTypes":"","count":100,"after":"","returnAll":false,"connection":"<connection id>"}}
```

## `hubspot.listOwners@1` — HubSpot — List owners

Lists the HubSpot users a record can be assigned to.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `email` | text, default `""` | E-mail: Leave empty for everyone. Fill it in to find one person's owner id. — its `{{ }}` must give a string |
| `archived` | true/false, default `false` | Include people who left |
| `limit` | number, default `100` | Owners per call: 1 to 500. Assign ownership with an owner's id, never with its userId. |
| `after` | text, default `""` | Start after: A paging cursor from an earlier run of this node. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows HubSpot's paging cursor, and refuses past 5,000 owners rather than answering a short list. |

Output: `owners` array — Owners · `ownerCount` number — Owners in this answer · `hasMore` boolean — More owners are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"hubspot.listOwners","version":1,"label":"List owners","config":{"email":"","archived":false,"limit":100,"after":"","returnAll":false,"connection":"<connection id>"}}
```

## `hubspot.listPipelines@1` — HubSpot — List pipelines

Lists an object's HubSpot pipelines and, if asked, their stages.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"deals"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. — used when `object` is "custom" |
| `includeStages` | true/false, default `true` | Include the stages: Reads each pipeline's stages as well — one extra call per pipeline, and this node refuses past 50 of them. |

Output: `pipelines` array — Pipelines · `pipelineCount` number — How many there are.

```json
{"type":"hubspot.listPipelines","version":1,"label":"List pipelines","config":{"object":"deals","customObjectType":"","includeStages":true,"connection":"<connection id>"}}
```

## `hubspot.listProperties@1` — HubSpot — List properties

Lists the fields one HubSpot object has, with their internal names.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. — used when `object` is "custom" |
| `archived` | true/false, default `false` | Include the ones that were removed |

Output: `properties` array — Properties · `propertyCount` number — How many there are.

```json
{"type":"hubspot.listProperties","version":1,"label":"List properties","config":{"object":"contacts","customObjectType":"","archived":false,"connection":"<connection id>"}}
```

## `hubspot.listRecords@1` — HubSpot — List records

Lists HubSpot records of one object, newest ids last.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. The List properties node shows it. — used when `object` is "custom" |
| `properties` | text, default `""` | Properties to fetch: Comma-separated. HubSpot leaves out anything you do not ask for. |
| `associations` | text, default `""` | Associated objects: Comma-separated object names to return associated ids for. |
| `archived` | true/false, default `false` | Look in the recycling bin |
| `limit` | number, default `100` | Records per call: 1 to 100. HubSpot's own default is 10, so this is always sent. |
| `after` | text, default `""` | Start after: A paging cursor from an earlier run of this node. Leave empty to start over. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows HubSpot's paging cursor, and refuses past 5,000 records rather than answering a short list. |

Output: `records` array — Records · `recordCount` number — Records in this answer · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"hubspot.listRecords","version":1,"label":"List records","config":{"object":"contacts","customObjectType":"","properties":"","associations":"","archived":false,"limit":100,"after":"","returnAll":false,"connection":"<connection id>"}}
```

## `hubspot.searchRecords@1` — HubSpot — Search records

Finds HubSpot records by property filters, sorted, with paging.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. The List properties node shows it. — used when `object` is "custom" |
| `query` | text, default `""` | Free text: Searches the object's default searchable properties. Leave empty to filter only. — its `{{ }}` must give a string |
| `filters` | list of {group?: "1" or "2" or "3" or "4" or "5", propertyName?: text, operator?: "EQ" or "NEQ" or "LT" or "LTE" or "GT" or "GTE" or "BETWEEN" or "IN" or "NOT_IN" or "HAS_PROPERTY" or "NOT_HAS_PROPERTY" or "CONTAINS_TOKEN" or "NOT_CONTAINS_TOKEN", value?: text, highValue?: text}, default `[]` | Filters: One object per filter. Rows with the same group are ANDed together; different groups are ORed. Values may carry {{ }}, so a filter can come from an earlier node. |
| `sortProperty` | text, default `""` | Sort by: One property only — HubSpot accepts exactly one sort per search. |
| `sortDirection` | "ASCENDING" or "DESCENDING", default `"DESCENDING"` | Direction |
| `properties` | text, default `""` | Properties to fetch: Comma-separated. HubSpot leaves out anything you do not ask for. |
| `limit` | number, default `100` | Records per call: 1 to 200. HubSpot's own default is 10, so this is always sent. |
| `after` | text, default `""` | Start after: A whole number: how many results to skip. HubSpot never answers past the 10,000th result of one search. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows HubSpot's paging cursor, and refuses past 5,000 records rather than answering a short list. |

Output: `records` array — Records · `recordCount` number — Records in this answer · `total` number — How many HubSpot counted · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"hubspot.searchRecords","version":1,"label":"Search records","config":{"object":"contacts","customObjectType":"","query":"","filters":[],"sortProperty":"","sortDirection":"DESCENDING","properties":"","limit":100,"after":"","returnAll":false,"connection":"<connection id>"}}
```

## `hubspot.updateListMembership@1` — HubSpot — Change list membership

Adds records to a HubSpot list, removes them, or both.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `listId` | text, required | List id: The List lists node gives you the id. — its `{{ }}` must give a string |
| `mode` | "add" or "remove" or "addAndRemove", default `"add"` | What to do: Only manual and snapshot lists can be changed this way — a dynamic list is maintained by its own filters. |
| `recordIdsToAdd` | any JSON, optional | Ids to add — its `{{ }}` must give an array; used when `mode` is one of "add", "addAndRemove" |
| `recordIdsToRemove` | any JSON, optional | Ids to remove — its `{{ }}` must give an array; used when `mode` is one of "remove", "addAndRemove" |

Output: `listId` string — List id · `recordsAdded` number — Ids sent to be added · `recordsRemoved` number — Ids sent to be removed.

```json
{"type":"hubspot.updateListMembership","version":1,"label":"Change list membership","config":{"mode":"add","connection":"<connection id>","listId":"<list id>"}}
```

## `hubspot.updateRecord@1` — HubSpot — Update record

Changes properties on one HubSpot record.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. The List properties node shows it. — used when `object` is "custom" |
| `recordId` | text, required | Record id: The record's HubSpot id, or the value of the unique property named below. — its `{{ }}` must give a string |
| `idProperty` | text, default `""` | Look up by property: Leave empty to address the record by its HubSpot record id. Fill in email to address a contact by e-mail, or the internal name of any unique-identifier property. |
| `properties` | object (any keys), default `{}` | Properties: One row per property, keyed by its internal name. Set hs_pinned_engagement_id to pin an activity to the top of the record. |

Output: `recordId` string — Record id · `record` object — Record · `properties` object — Properties.

```json
{"type":"hubspot.updateRecord","version":1,"label":"Update record","config":{"object":"contacts","customObjectType":"","idProperty":"","properties":{},"connection":"<connection id>","recordId":"<record id>"}}
```

## `hubspot.uploadFile@1` — HubSpot — Upload file

Puts a file in the HubSpot file manager.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `name` | text, required | File name: The file's name, extension included. Pick the file, then paste its base64 below. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("name").base64 }}. — its `{{ }}` must give a string |
| `access` | "PRIVATE" or "PUBLIC_INDEXABLE" or "PUBLIC_NOT_INDEXABLE", default `"PRIVATE"` | Who may open it: A private file needs a signed URL before anything can read it back. Public files are readable by anyone who has the address. |
| `folderPath` | text, default `"/flowmage"` | Folder: Where in the file manager it lands. HubSpot creates the folder if it is not there yet. |
| `folderId` | text, default `""` | Folder id: An existing folder's id instead of a path. HubSpot takes one or the other — fill this in and clear the Folder above. |
| `ttl` | text, default `""` | Delete it again after: Leave empty to keep the file. Otherwise HubSpot's own duration, between one day and one year, written the way its Files API documents it. |

Output: `file` object — File · `fileId` string — File id · `url` string — Address · `access` string — Who may open it.

```json
{"type":"hubspot.uploadFile","version":1,"label":"Upload file","config":{"access":"PRIVATE","folderPath":"/flowmage","folderId":"","ttl":"","connection":"<connection id>","name":"<file name>","contentBase64":"<file contents>"}}
```

## `hubspot.upsertRecord@1` — HubSpot — Create or update record

Creates a HubSpot record, or updates the one that already has this key.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `hubspot`) |
| `object` | "contacts" or "companies" or "deals" or "tickets" or "notes" or "tasks" or "calls" or "meetings" or "emails" or "leads" or "products" or "line_items" or "custom", default `"contacts"` | Object |
| `customObjectType` | text, default `""` | Custom object type id: The object type id, e.g. 2-1234567. The List properties node shows it. — used when `object` is "custom" |
| `idProperty` | text, default `"email"` | Key property: The unique-identifier property HubSpot matches on — email for contacts, or any property marked as a unique identifier. HubSpot does not support partial upserts when the key is email on contacts, so every property you leave out stays as it was only because this node always sends the whole set. |
| `idValue` | text, required | Key value — its `{{ }}` must give a string |
| `properties` | object (any keys), default `{}` | Properties: One row per property, keyed by its internal name. |

Output: `recordId` string — Record id · `record` object — Record.

```json
{"type":"hubspot.upsertRecord","version":1,"label":"Create or update record","config":{"object":"contacts","customObjectType":"","idProperty":"email","properties":{},"connection":"<connection id>","idValue":"<key value>"}}
```


---

## File: references/nodes/human.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# human nodes (`human.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `human.approve@1` — Ask for approval

Stops the flow until a person picks one of your options, or the time runs out.

Ports: one per row `id` in `options`, plus `timeout` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `options` | list of {id: text, label: text}, default `[{"id":"approved","label":"Approve"},{"id":"rejected","label":"Reject"}]` | Options: Between 2 and 5 answers. Each one is a button and an output port; renaming the words keeps the port. |
| `request.title` | text, default `"Approval needed"` | Title: One line, at most 120 characters — it is the subject of the mail and the heading of the page. |
| `request.body` | text, default `""` | What you are asking: Up to 4000 characters. Secrets this run resolved are redacted before anybody sees it. |
| `request.data` | object (any keys), default `{}` | Details the approver sees: The snapshot on the page — a value per line, expressions allowed. |
| `request.requireComment` | true/false, default `false` | Ask for a comment: One rule for every option: the person must type a reason before they answer. |
| `request.approverMembers` | list of text, optional | Who decides: At most 5 people in total, counting any addresses below. Somebody who has left the organization is asked nothing. |
| `request.approverAddresses` | text, default `""` | Addresses outside the workspace: Each address must already be in this workspace's address book (/alerts). A confirmed one is emailed a one-time link; one that has not confirmed is asked to confirm first. — used when `request.whoMayDecide` is "members-and-outside" |
| `request.whoMayDecide` | "members" or "members-and-outside", default `"members"` | May somebody outside the workspace decide?: Forwarding the mail delegates the decision — whoever opens the link answers, and the record says which address it was. |
| `deadline.amount` | number, default `7` | Wait at most: A number, or a {{ }} expression that resolves to one. |
| `deadline.unit` | "minutes" or "hours" or "days", default `"days"` | Unit: From 1 minute to 31 days. A waiting run costs nothing while it waits and keeps its own plan; a connection or secret deleted meanwhile fails the next node cleanly. |
| `onTimeout` | "continue" or "fail", default `"continue"` | If nobody answers in time: Every request ends: either a person answers it, or the deadline does. |

Output: shaped by the config.

```json
{"type":"human.approve","version":1,"label":"Ask for approval","config":{"options":[{"id":"approved","label":"Approve"},{"id":"rejected","label":"Reject"}],"request":{"title":"Approval needed","body":"","data":{},"requireComment":false,"approverAddresses":"","whoMayDecide":"members","approverMembers":[]},"deadline":{"amount":7,"unit":"days"},"onTimeout":"continue"}}
```


---

## File: references/nodes/jira.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Jira nodes (`jira.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `jira.addComment@1` — Jira — Add comment

Adds a comment to a Jira issue.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `bodyFormat` | "text" or "adf", default `"text"` | Comment format |
| `body` | text, default `""` | Comment: A blank line starts a new paragraph and a single newline becomes a line break; nothing else is interpreted. — used when `bodyFormat` is "text" |
| `bodyAdf` | object (any keys), default `{}` | Comment (ADF): A full Atlassian Document Format document: { "type": "doc", "version": 1, "content": [ … ] }. — used when `bodyFormat` is "adf" |
| `visibilityType` | "" or "role" or "group", default `""` | Visible to: A role or a group restricts it — the name must match a Jira project role or group exactly. |
| `visibilityValue` | text, default `""` | Role or group name — used when `visibilityType` is not "" |
| `jsdPublic` | true/false, default `true` | The customer may see it: On a Jira Service Management project, off makes this an internal note the customer does not see. It is ignored on a plain Jira project. |

Output: `commentId` string — Comment id · `comment` object — Comment · `issueRef` string — Issue key.

```json
{"type":"jira.addComment","version":1,"label":"Add comment","config":{"bodyFormat":"text","body":"","bodyAdf":{},"visibilityType":"","visibilityValue":"","jsdPublic":true,"connection":"<connection id>","issue":"<issue key>"}}
```

## `jira.addWorklog@1` — Jira — Log work

Logs time spent on a Jira issue.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `timeSpent` | text, required | Time spent: Jira's own notation: 2d, 3h, 30m, or a combination. |
| `started` | text, default `""` | Started at: When the work started. Jira wants this exact shape, milliseconds and a numeric offset included; leave it empty and the node sends now. |
| `comment` | text, default `""` | Comment |
| `adjustEstimate` | "auto" or "leave" or "new" or "manual", default `"auto"` | What happens to the remaining estimate |
| `newEstimate` | text, default `""` | New remaining estimate — used when `adjustEstimate` is "new" |
| `reduceBy` | text, default `""` | Reduce the estimate by — used when `adjustEstimate` is "manual" |
| `notifyUsers` | true/false, default `true` | Notify watchers |
| `visibilityType` | "" or "role" or "group", default `""` | Visible to |
| `visibilityValue` | text, default `""` | Role or group name — used when `visibilityType` is not "" |

Output: `worklogId` string — Worklog id · `worklog` object — Worklog · `issueRef` string — Issue key.

```json
{"type":"jira.addWorklog","version":1,"label":"Log work","config":{"started":"","comment":"","adjustEstimate":"auto","newEstimate":"","reduceBy":"","notifyUsers":true,"visibilityType":"","visibilityValue":"","connection":"<connection id>","issue":"<issue key>","timeSpent":"<time spent>"}}
```

## `jira.assignIssue@1` — Jira — Assign issue

Assigns a Jira issue to a person, or unassigns it.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `assignTo` | "accountId" or "unassigned" or "default", default `"accountId"` | Assign to: Nobody is only possible when the Jira admin allows unassigned issues; the default hands it to whoever the project names. |
| `accountId` | text, default `""` | Account id: Jira Cloud identifies a person by account id, never by username or e-mail. The Find users node gives you one. — used when `assignTo` is "accountId" |

Output: `issueRef` string — Issue key · `assigned` boolean — Jira accepted the change · `accountId` string — Who it went to.

```json
{"type":"jira.assignIssue","version":1,"label":"Assign issue","config":{"assignTo":"accountId","accountId":"","connection":"<connection id>","issue":"<issue key>"}}
```

## `jira.createIssue@1` — Jira — Create issue

Creates a Jira issue in a project and issue type you choose.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `project` | text, required | Project key: The key in front of every issue number, for example ENG in ENG-142. The List projects node gives you the keys. — a value from the vendor's own list |
| `issueType` | text, required | Issue type: The name as Jira shows it — Task, Bug, Story, Sub-task. The List issue types node gives you the names this project allows. — a value from the vendor's own list |
| `summary` | text, required | Summary |
| `descriptionFormat` | "text" or "adf" or "none", default `"text"` | Description format |
| `description` | text, default `""` | Description: Jira stores rich text as a JSON document. A blank line starts a new paragraph and a single newline becomes a line break; nothing else is interpreted. — used when `descriptionFormat` is "text" |
| `descriptionAdf` | object (any keys), default `{}` | Description (ADF): A full Atlassian Document Format document: { "type": "doc", "version": 1, "content": [ … ] }. — used when `descriptionFormat` is "adf" |
| `assigneeAccountId` | text, default `""` | Assignee account id: Jira Cloud identifies people by account id, never by username or e-mail. The Find users node gives you one. |
| `reporterAccountId` | text, default `""` | Reporter account id |
| `priority` | text, default `""` | Priority: The name as Jira shows it. Leave empty to use the project's default. |
| `labels` | text, default `""` | Labels: Comma-separated. A Jira label may not contain a space. |
| `dueDate` | text, default `""` | Due date |
| `parent` | text, default `""` | Parent issue key: For a sub-task, or for an issue under an epic on a team-managed project. |
| `components` | text, default `""` | Components: Comma-separated names. |
| `fixVersions` | text, default `""` | Fix versions: Comma-separated names. |
| `customFields` | object of text, default `{}` | Custom fields: Jira keys a custom field by its id — customfield_10034, never its title. The List fields node gives you the ids. Values are sent as written; a field that wants an object needs the escape below. |
| `extraFields` | object (any keys), default `{}` | Extra fields (raw JSON): Merged into the fields object verbatim, for a field whose value is an object or an array — { "customfield_10050": { "value": "Red" } }. Anything here wins over the fields above. |

Output: `issueRef` string — Issue key · `issueId` string — Issue id · `issue` object — Issue · `issueUrl` string — Link to the issue.

```json
{"type":"jira.createIssue","version":1,"label":"Create issue","config":{"descriptionFormat":"text","description":"","descriptionAdf":{},"assigneeAccountId":"","reporterAccountId":"","priority":"","labels":"","dueDate":"","parent":"","components":"","fixVersions":"","customFields":{},"extraFields":{},"connection":"<connection id>","project":"<project key>","issueType":"<issue type>","summary":"<summary>"}}
```

## `jira.deleteIssue@1` — Jira — Delete issue

Deletes a Jira issue, and optionally its sub-tasks.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `deleteSubtasks` | true/false, default `false` | Delete its sub-tasks too: Jira refuses to delete an issue that has sub-tasks unless you switch this on. Deletion is permanent — Jira has no trash for issues. |

Output: `issueRef` string — Issue key · `deleted` boolean — This run deleted it · `alreadyGone` boolean — It was already gone.

```json
{"type":"jira.deleteIssue","version":1,"label":"Delete issue","config":{"deleteSubtasks":false,"connection":"<connection id>","issue":"<issue key>"}}
```

## `jira.findUsers@2` — Jira — Find users

Finds Jira users, or the people who can be assigned an issue.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `scope` | "all" or "assignable", default `"all"` | Who to look through |
| `query` | text, default `""` | Name or e-mail contains: Jira Cloud identifies a person by account id; an e-mail address is often hidden by that person's Atlassian privacy setting, so a search on an address can legitimately find nobody. |
| `project` | text, default `""` | Project key — a value from the vendor's own list; used when `scope` is "assignable" |
| `issue` | text, default `""` | Issue key: Either a project key or an issue key — Jira needs one of the two. — used when `scope` is "assignable" |
| `limit` | number, default `50` | People per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Jira's offset pages, and refuses past 5,000 people. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `users` array — People · `userCount` number — People in this answer · `accountIds` object — Display name → account id · `hasMore` boolean — More people are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"jira.findUsers","version":2,"label":"Find users","config":{"scope":"all","query":"","project":"","issue":"","limit":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `jira.findUsers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `scope` | "all" or "assignable", default `"all"` | Who to look through |
| `query` | text, default `""` | Name or e-mail contains: Jira Cloud identifies a person by account id; an e-mail address is often hidden by that person's Atlassian privacy setting, so a search on an address can legitimately find nobody. |
| `project` | text, default `""` | Project key — a value from the vendor's own list; used when `scope` is "assignable" |
| `issue` | text, default `""` | Issue key: Either a project key or an issue key — Jira needs one of the two. — used when `scope` is "assignable" |
| `limit` | number, default `50` | People per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Jira's offset pages, and refuses past 5,000 people. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"scope":"all","query":"","project":"","issue":"","limit":50,"returnAll":false,"connection":"<connection id>"}
{"scope":"all","query":"","project":"","issue":"","limit":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `jira.getIssue@1` — Jira — Get issue

Reads one Jira issue and the fields you ask for.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `fields` | text, default `"summary,status,assignee,priority,labels,duedate"` | Fields to fetch: Jira returns nothing you do not ask for. *all returns everything and is slow on a big issue. |
| `expand` | text, default `""` | Expand |
| `fieldsByKeys` | true/false, default `false` | Name custom fields by their key: Off, Jira names them by id — customfield_10034. |
| `plainTextDescription` | true/false, default `true` | Also give the description as plain text: Jira stores a description as a JSON document. This adds the words beside it, so you do not have to walk the tree. |

Output: `issue` object — Issue · `issueRef` string — Issue key · `issueId` string — Issue id · `issueUrl` string — Link to the issue · `fields` object — Fields · `descriptionText` string — Description in words · `status` string — Status.

```json
{"type":"jira.getIssue","version":1,"label":"Get issue","config":{"fields":"summary,status,assignee,priority,labels,duedate","expand":"","fieldsByKeys":false,"plainTextDescription":true,"connection":"<connection id>","issue":"<issue key>"}}
```

## `jira.linkIssues@1` — Jira — Link issues

Links two Jira issues with a relationship.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `linkType` | text, required | Relationship: With the type Blocks, the OUTWARD issue blocks the INWARD one. Jira's own link dropdown shows both wordings. |
| `inwardIssue` | text, required | That issue: The one on the receiving end — it is blocked, cloned or duplicated. |
| `outwardIssue` | text, required | This issue: The one the relationship starts from — it blocks, clones or duplicates. |
| `comment` | text, default `""` | Comment: Added to the issue as part of the same call. |

Output: `linked` boolean — Jira created the link · `linkType` string — Relationship · `inwardIssue` string — That issue · `outwardIssue` string — This issue.

```json
{"type":"jira.linkIssues","version":1,"label":"Link issues","config":{"comment":"","connection":"<connection id>","linkType":"<relationship>","inwardIssue":"<that issue>","outwardIssue":"<this issue>"}}
```

## `jira.listComments@2` — Jira — List comments

Reads the comments on a Jira issue.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `order` | "created" or "-created", default `"created"` | Order |
| `plainText` | true/false, default `true` | Also give each comment as plain text: Jira stores a comment as a JSON document. This adds the words beside it as bodyText. |
| `renderedBody` | true/false, default `false` | Also give Jira's own HTML rendering |
| `limit` | number, default `100` | Comments per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Jira's offset pages, and refuses past 5,000 comments. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `comments` array — Comments · `commentCount` number — Comments in this answer · `total` number — Comments Jira counted · `hasMore` boolean — More comments are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"jira.listComments","version":2,"label":"List comments","config":{"order":"created","plainText":true,"renderedBody":false,"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","issue":"<issue key>"}}
```

### Older version `jira.listComments@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `order` | "created" or "-created", default `"created"` | Order |
| `plainText` | true/false, default `true` | Also give each comment as plain text: Jira stores a comment as a JSON document. This adds the words beside it as bodyText. |
| `renderedBody` | true/false, default `false` | Also give Jira's own HTML rendering |
| `limit` | number, default `100` | Comments per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Jira's offset pages, and refuses past 5,000 comments. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"order":"created","plainText":true,"renderedBody":false,"limit":100,"returnAll":false,"connection":"<connection id>","issue":"<issue key>"}
{"order":"created","plainText":true,"renderedBody":false,"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","issue":"<issue key>"}
```

## `jira.listFields@1` — Jira — List fields

Lists every Jira field with its id, including custom fields.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `customOnly` | true/false, default `true` | Only custom fields: Off also lists Jira's built-in fields — summary, status, assignee and the rest. |
| `nameContains` | text, default `""` | Name contains: Case does not matter. |

Output: `fields` array — Fields · `fieldCount` number — Fields in this answer · `fieldIds` object — Field name → its id · `duplicateNames` array — Names that belong to more than one field.

```json
{"type":"jira.listFields","version":1,"label":"List fields","config":{"customOnly":true,"nameContains":"","connection":"<connection id>"}}
```

## `jira.listIssueTypes@1` — Jira — List issue types

Lists the issue types a Jira project lets you create.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `project` | text, required | Project key — a value from the vendor's own list |
| `withFields` | true/false, default `false` | Also list the fields each type asks for: One extra read per type, and which of them are required. Capped at the first 20 types. |

Output: `issueTypes` array — Issue types · `issueTypeCount` number — Issue types in this answer · `issueTypeNames` array — Their names · `requiredFields` object — Type name → the ids it requires.

```json
{"type":"jira.listIssueTypes","version":1,"label":"List issue types","config":{"withFields":false,"connection":"<connection id>","project":"<project key>"}}
```

## `jira.listProjects@2` — Jira — List projects

Lists Jira projects and their keys.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `query` | text, default `""` | Name or key contains: Matches a project's name or its key. |
| `keys` | text, default `""` | Only these keys: Comma-separated project keys. |
| `projectType` | "" or "software" or "service_desk" or "business", default `""` | Kind |
| `orderBy` | "key" or "name" or "-name" or "lastIssueUpdatedTime", default `"key"` | Order by |
| `limit` | number, default `100` | Projects per call: 1 to 100 — Jira's own maximum on this endpoint. |
| `returnAll` | true/false, default `true` | Fetch every page: Walks Jira's offset pages, and refuses past 5,000 projects. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `projects` array — Projects · `projectCount` number — Projects in this answer · `projectKeys` object — Project name → its key · `hasMore` boolean — More projects are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"jira.listProjects","version":2,"label":"List projects","config":{"query":"","keys":"","projectType":"","orderBy":"key","limit":100,"returnAll":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `jira.listProjects@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `query` | text, default `""` | Name or key contains: Matches a project's name or its key. |
| `keys` | text, default `""` | Only these keys: Comma-separated project keys. |
| `projectType` | "" or "software" or "service_desk" or "business", default `""` | Kind |
| `orderBy` | "key" or "name" or "-name" or "lastIssueUpdatedTime", default `"key"` | Order by |
| `limit` | number, default `100` | Projects per call: 1 to 100 — Jira's own maximum on this endpoint. |
| `returnAll` | true/false, default `true` | Fetch every page: Walks Jira's offset pages, and refuses past 5,000 projects. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"query":"","keys":"","projectType":"","orderBy":"key","limit":100,"returnAll":true,"connection":"<connection id>"}
{"query":"","keys":"","projectType":"","orderBy":"key","limit":100,"returnAll":true,"cursor":"","connection":"<connection id>"}
```

## `jira.listTransitions@1` — Jira — List transitions

Lists the moves this Jira issue can make from where it is.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `includeFields` | true/false, default `false` | Also list what each move asks for: A transition can have a screen of its own — this says which fields it wants and which are required. |
| `includeUnavailable` | true/false, default `false` | Include moves this account may not make |

Output: `transitions` array — Transitions · `transitionCount` number — Transitions in this answer · `transitionIds` object — Transition name → its id · `toStatuses` object — Transition name → the status it moves to.

```json
{"type":"jira.listTransitions","version":1,"label":"List transitions","config":{"includeFields":false,"includeUnavailable":false,"connection":"<connection id>","issue":"<issue key>"}}
```

## `jira.searchIssues@2` — Jira — Search issues

Finds Jira issues with a JQL query.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `jql` | text, required | JQL: Jira refuses a query that is not bounded — one that is only an ORDER BY names no issues and is rejected. An ORDER BY may name at most 7 fields. |
| `fields` | text, default `"summary,status,assignee,priority,updated"` | Fields to fetch: Jira returns nothing you do not ask for. |
| `expand` | text, default `""` | Expand |
| `fieldsByKeys` | true/false, default `false` | Name custom fields by their key |
| `includeArchivedProjects` | true/false, default `false` | Include archived projects |
| `limit` | number, default `50` | Issues per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Jira's next-page token until it stops sending one. At Pages at most or 5,000 issues it stops and answers the token to continue from. A token expires after 7 days, so one carried across a parked run is dead. |
| `maxPages` | number, default `20` | Pages at most: 1 to 100. — used when `returnAll` is true |
| `withCount` | true/false, default `false` | Also count the matches: Jira no longer returns a total from a search, so this costs one extra call and the number it gives is approximate. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `issues` array — Issues · `issueCount` number — Issues in this answer · `hasMore` boolean — More issues are waiting behind this page · `nextPage` string — Jira's token for the next page · `approximateTotal` number — About how many match (when asked for) · `pages` number — Requests this read took · `nextCursor` string — Cursor for the next page.

```json
{"type":"jira.searchIssues","version":2,"label":"Search issues","config":{"fields":"summary,status,assignee,priority,updated","expand":"","fieldsByKeys":false,"includeArchivedProjects":false,"limit":50,"returnAll":false,"maxPages":20,"withCount":false,"cursor":"","connection":"<connection id>","jql":"<jql>"}}
```

### Older version `jira.searchIssues@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `jql` | text, required | JQL: Jira refuses a query that is not bounded — one that is only an ORDER BY names no issues and is rejected. An ORDER BY may name at most 7 fields. |
| `fields` | text, default `"summary,status,assignee,priority,updated"` | Fields to fetch: Jira returns nothing you do not ask for. |
| `expand` | text, default `""` | Expand |
| `fieldsByKeys` | true/false, default `false` | Name custom fields by their key |
| `includeArchivedProjects` | true/false, default `false` | Include archived projects |
| `limit` | number, default `50` | Issues per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Jira's next-page token until it stops sending one. At Pages at most or 5,000 issues it stops and answers the token to continue from. A token expires after 7 days, so one carried across a parked run is dead. |
| `maxPages` | number, default `20` | Pages at most: 1 to 100. — used when `returnAll` is true |
| `withCount` | true/false, default `false` | Also count the matches: Jira no longer returns a total from a search, so this costs one extra call and the number it gives is approximate. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"fields":"summary,status,assignee,priority,updated","expand":"","fieldsByKeys":false,"includeArchivedProjects":false,"limit":50,"returnAll":false,"maxPages":20,"withCount":false,"connection":"<connection id>","jql":"<jql>"}
{"fields":"summary,status,assignee,priority,updated","expand":"","fieldsByKeys":false,"includeArchivedProjects":false,"limit":50,"returnAll":false,"maxPages":20,"withCount":false,"cursor":"","connection":"<connection id>","jql":"<jql>"}
```

## `jira.transitionIssue@1` — Jira — Move issue

Moves a Jira issue through a workflow transition.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `transitionBy` | "name" or "id", default `"name"` | Name the move by: A Jira status is not written directly: you name a transition, and which transitions exist depends on the workflow and on where the issue is now. A name costs one extra read and survives a workflow edit; an id does not. |
| `transition` | text, required | Move: The List transitions node shows both the names and the ids. |
| `comment` | text, default `""` | Comment: Added as part of the same move, so the change and its reason are one event. |
| `resolution` | text, default `""` | Resolution: Many workflows require a resolution on the move to Done — give its name, for example Done or Won't Do. |
| `fields` | object of text, default `{}` | Fields the move asks for: Keyed by field id — the List transitions node names them per move. |
| `extraFields` | object (any keys), default `{}` | Extra fields (raw JSON): For a field whose value is an object — { "customfield_10050": { "value": "Red" } }. |

Output: `issueRef` string — Issue key · `transitionId` string — Transition id · `transitionName` string — Transition name · `toStatus` string — The status it moved to.

```json
{"type":"jira.transitionIssue","version":1,"label":"Move issue","config":{"transitionBy":"name","comment":"","resolution":"","fields":{},"extraFields":{},"connection":"<connection id>","issue":"<issue key>","transition":"<move>"}}
```

## `jira.updateIssue@1` — Jira — Update issue

Changes fields on a Jira issue.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `summary` | text, default `""` | Summary |
| `descriptionFormat` | "none" or "text" or "adf", default `"none"` | Description format |
| `description` | text, default `""` | Description: A blank line starts a new paragraph and a single newline becomes a line break; nothing else is interpreted. — used when `descriptionFormat` is "text" |
| `descriptionAdf` | object (any keys), default `{}` | Description (ADF): A full Atlassian Document Format document: { "type": "doc", "version": 1, "content": [ … ] }. — used when `descriptionFormat` is "adf" |
| `assigneeAccountId` | text, default `""` | Assignee account id: The Find users node gives you an account id. |
| `priority` | text, default `""` | Priority |
| `labels` | text, default `""` | Labels: Comma-separated, and Jira REPLACES the whole list with what you send. A label may not contain a space. |
| `dueDate` | text, default `""` | Due date |
| `parent` | text, default `""` | Parent issue key |
| `components` | text, default `""` | Components: Comma-separated names. |
| `fixVersions` | text, default `""` | Fix versions: Comma-separated names. |
| `customFields` | object of text, default `{}` | Custom fields: Keyed by id — customfield_10034. The List fields node gives you the ids. |
| `extraFields` | object (any keys), default `{}` | Extra fields (raw JSON): Merged into the fields object verbatim, for a field whose value is an object or an array — { "customfield_10050": { "value": "Red" } }. |
| `historyComment` | text, default `""` | Comment with this change: Added in the same update, so the change and its reason are one event. |
| `notifyUsers` | true/false, default `true` | Notify watchers: On, a second run sends a second notification even when nothing actually changed. |
| `returnIssue` | true/false, default `false` | Read the issue back: Jira answers nothing at all unless you ask for the issue. |

Output: `issueRef` string — Issue key · `updated` boolean — Jira accepted the change · `issue` object — Issue (only when read back).

```json
{"type":"jira.updateIssue","version":1,"label":"Update issue","config":{"summary":"","descriptionFormat":"none","description":"","descriptionAdf":{},"assigneeAccountId":"","priority":"","labels":"","dueDate":"","parent":"","components":"","fixVersions":"","customFields":{},"extraFields":{},"historyComment":"","notifyUsers":true,"returnIssue":false,"connection":"<connection id>","issue":"<issue key>"}}
```

## `jira.uploadAttachment@1` — Jira — Upload attachment

Attaches a file to a Jira issue.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `jira`) |
| `issue` | text, required | Issue key |
| `name` | text, required | File name: The file's name, extension included. Pick the file, then paste its base64 below. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("report.pdf").base64 }}. The largest file Jira accepts is a setting of your Jira site, which a Jira admin changes. — its `{{ }}` must give a string |

Output: `attachments` array — Attachments Jira stored · `attachmentId` string — Attachment id · `attachmentUrl` string — Link to the attachment.

```json
{"type":"jira.uploadAttachment","version":1,"label":"Upload attachment","config":{"connection":"<connection id>","issue":"<issue key>","name":"<file name>","contentBase64":"<file contents>"}}
```


---

## File: references/nodes/json.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# json nodes (`json.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `json.build@1` — JSON — Build

Builds an object from the fields you fill in — for an API body or a mapping.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `fields` | object (any keys), default `{}` | Fields: A value can come from an earlier step; one that is a single reference keeps its type. |

Output: shaped by the config.

```json
{"type":"json.build","version":1,"label":"Build","config":{"fields":{}}}
```

## `json.parse@1` — JSON — Parse

Turns JSON text into data the rest of the flow can read.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `text` | any JSON, optional | JSON text: Text that CONTAINS JSON — an already-parsed value needs no parse node. — its `{{ }}` must give a string |

Output: depends on the data.

```json
{"type":"json.parse","version":1,"label":"Parse","config":{"text":"<json text>"}}
```

## `json.stringify@1` — JSON — Stringify

Makes JSON text out of any value, compact or indented.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `value` | any JSON, optional | Value: The result is under text. |
| `indent` | "2" or "4" or "compact" or "tab", default `"compact"` | Indentation: Compact is one line — indent when a person will read the output. |
| `sortKeys` | true/false, default `false` | Sort keys: Sorts keys at every level, so the same data always produces the same text. |

Output: `text` string — JSON text.

```json
{"type":"json.stringify","version":1,"label":"Stringify","config":{"indent":"compact","sortKeys":false}}
```


---

## File: references/nodes/list.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# list nodes (`list.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `list.dedupe@1` — List — Remove duplicates

Removes duplicate rows, on the fields you choose or on the whole row.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `rows` | any JSON, optional | Rows: The list to clean up. — its `{{ }}` must give an array |
| `fields` | list of {field: text}, default `[]` | Fields: Rows that share these values become one. No fields removes exact duplicates instead. |

Output: `rows` array — Rows · `count` number — Row count · `removed` number — Duplicates removed.

```json
{"type":"list.dedupe","version":1,"label":"Remove duplicates","config":{"fields":[],"rows":"<rows>"}}
```

## `list.filter@1` — List — Filter

Keeps only the rows that match your conditions.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `rows` | any JSON, optional | Rows: The list to filter. — its `{{ }}` must give an array |
| `combine` | "and" or "or", default `"and"` | Match: Whether every condition must hold, or one match is enough. |
| `conditions` | list of {field: text, operator?: "equals" or "notEquals" or "contains" or "notContains" or "startsWith" or "endsWith" or "greaterThan" or "greaterOrEqual" or "lessThan" or "lessOrEqual" or "isEmpty" or "isNotEmpty", value?: text}, default `[]` | Conditions: No conditions keeps every row. Numbers compare as numbers, ISO dates as dates, everything else as text. |

Output: `rows` array — Rows · `count` number — Row count.

```json
{"type":"list.filter","version":1,"label":"Filter","config":{"combine":"and","conditions":[],"rows":"<rows>"}}
```

## `list.group@1` — List — Group

Groups rows by a field and counts, sums or averages each group.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `rows` | any JSON, optional | Rows: The list to total up. — its `{{ }}` must give an array |
| `groupBy` | text, default `""` | Group by: The field whose values become one row each. Rows with no value for it group together. |
| `aggregates` | list of {field: text, function?: "sum" or "avg" or "min" or "max"}, default `[]` | Totals: Every group carries count. A result column is named field_function, for example total_sum. |

Output: `rows` array — Groups · `count` number — Group count.

```json
{"type":"list.group","version":1,"label":"Group","config":{"groupBy":"","aggregates":[],"rows":"<rows>"}}
```

## `list.limit@1` — List — Limit

Cuts a list down to the first few rows and says how many there were.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `rows` | any JSON, optional | Rows: The list to cut short — sort it first if 'first' means something else. — its `{{ }}` must give an array |
| `max` | number, default `100` | Keep: The first N rows, in their incoming order. At least 1. |

Output: `rows` array — Rows · `count` number — Kept rows · `total` number — Rows that came in.

```json
{"type":"list.limit","version":1,"label":"Limit","config":{"max":100,"rows":"<rows>"}}
```

## `list.sort@1` — List — Sort

Orders rows by the fields you choose, with numbers and dates in real order.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `rows` | any JSON, optional | Rows: The list to order. — its `{{ }}` must give an array |
| `sort` | list of {field: text, direction?: "asc" or "desc"}, default `[]` | Sort by: The first field decides; a second field breaks its ties. Rows with no value sort last. No fields keeps the incoming order. |

Output: `rows` array — Rows · `count` number — Row count.

```json
{"type":"list.sort","version":1,"label":"Sort","config":{"sort":[],"rows":"<rows>"}}
```


---

## File: references/nodes/mailchimp.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Mailchimp nodes (`mailchimp.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `mailchimp.addMember@1` — Mailchimp — Add contact

Adds a new contact to an audience and fails when the address is already there.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `status` | "subscribed" or "unsubscribed" or "pending" or "transactional" or "cleaned", default `"subscribed"` | Status: Use Add or update contact unless you specifically want the flow to fail when the address is already there. |
| `tags` | text, default `""` | Tags: Comma separated. This is the one thing Add or update contact cannot do: tags at creation. |
| `emailType` | "" or "html" or "text", default `""` | E-mail format |
| `mergeFields` | object of text, default `{}` | Merge fields: Keyed by the merge TAG — FNAME, not First name. |
| `interests` | object of true/false, default `{}` | Interest groups: Keyed by the interest id — the List interests node gives you the ids. |
| `language` | text, default `""` | Language |
| `vip` | "" or "on" or "off", default `""` | VIP |
| `latitude` | text, default `""` | Latitude |
| `longitude` | text, default `""` | Longitude |
| `marketingPermissions` | list of any JSON, default `[]` | Marketing permissions: One object per permission; the audience defines the ids. |
| `ipSignup` | text, default `""` | Sign-up IP address |
| `timestampSignup` | text, default `""` | Sign-up moment |
| `ipOpt` | text, default `""` | Opt-in IP address |
| `timestampOpt` | text, default `""` | Opt-in moment |
| `skipMergeValidation` | true/false, default `false` | Accept a missing merge field: Accepts the contact even when a required merge field is missing. |

Output: `member` object — Contact · `memberId` string — Contact id · `email` string — E-mail address · `status` string — Status · `tags` array — Tags · `mergeFields` object — Merge fields.

```json
{"type":"mailchimp.addMember","version":1,"label":"Add contact","config":{"audienceId":"","email":"","status":"subscribed","tags":"","emailType":"","mergeFields":{},"interests":{},"language":"","vip":"","latitude":"","longitude":"","marketingPermissions":[],"ipSignup":"","timestampSignup":"","ipOpt":"","timestampOpt":"","skipMergeValidation":false,"connection":"<connection id>"}}
```

## `mailchimp.addMemberEvent@1` — Mailchimp — Add contact event

Writes a custom event onto a contact so a journey can branch on it.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `name` | text, default `""` | Event name: 2 to 30 characters. This is the name a journey's condition matches on. |
| `properties` | object of text, default `{}` | Event properties: Mailchimp stores event properties as text. |
| `occurredAt` | text, default `""` | Happened at: ISO 8601. Left empty, Mailchimp uses the moment it receives the event. |
| `isSyncing` | true/false, default `false` | Do not fire journeys: The journeys watching this event will not fire. |

Output: `written` boolean — Event written · `name` string — Event name · `email` string — E-mail address.

```json
{"type":"mailchimp.addMemberEvent","version":1,"label":"Add contact event","config":{"audienceId":"","email":"","name":"","properties":{},"occurredAt":"","isSyncing":false,"connection":"<connection id>"}}
```

## `mailchimp.addMemberNote@1` — Mailchimp — Add contact note

Adds a note of up to 1,000 characters to a contact.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `note` | text, default `""` | Note: Mailchimp stores at most 1,000 characters; a longer note is refused here. |

Output: `note` object — Note · `noteId` number — Note id · `email` string — E-mail address.

```json
{"type":"mailchimp.addMemberNote","version":1,"label":"Add contact note","config":{"audienceId":"","email":"","note":"","connection":"<connection id>"}}
```

## `mailchimp.archiveMember@1` — Mailchimp — Archive contact

Archives a contact so they stop receiving mail and stop counting on the plan.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |

Output: `archived` boolean — Archived · `wasPresent` boolean — The contact was still there · `email` string — E-mail address.

```json
{"type":"mailchimp.archiveMember","version":1,"label":"Archive contact","config":{"audienceId":"","email":"","connection":"<connection id>"}}
```

## `mailchimp.createCampaign@1` — Mailchimp — Create campaign

Creates a campaign for an audience, ready to be filled and sent.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `type` | "regular" or "plaintext" or "rss" or "variate" or "absplit", default `"regular"` | Type |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `savedSegmentId` | text, default `""` | Saved segment id: Send to one segment instead of the whole audience. |
| `subjectLine` | text, default `""` | Subject line |
| `previewText` | text, default `""` | Preview text |
| `title` | text, default `""` | Internal name: What you will recognise the campaign by inside Mailchimp. |
| `fromName` | text, default `""` | From name |
| `replyTo` | text, default `""` | Reply-to address: Mailchimp lets you create a campaign without it but refuses to send one. |
| `toName` | text, default `""` | To name |
| `templateId` | text, default `""` | Template id — a value from the vendor's own list |
| `folderId` | text, default `""` | Folder id |
| `contentType` | "" or "template" or "multichannel", default `""` | Content type |
| `trackOpens` | "" or "on" or "off", default `""` | Track opens |
| `trackHtmlClicks` | "" or "on" or "off", default `""` | Track clicks in HTML |
| `trackTextClicks` | "" or "on" or "off", default `""` | Track clicks in plain text |
| `googleAnalytics` | text, default `""` | Google Analytics tag |
| `rssFeedUrl` | text, default `""` | RSS feed address — used when `type` is "rss" |
| `rssFrequency` | "" or "daily" or "weekly" or "monthly", default `""` | RSS frequency — used when `type` is "rss" |
| `rssScheduleJson` | object (any keys), default `{}` | RSS schedule: Which hour and which days Mailchimp checks the feed. — used when `type` is "rss" |
| `variateSettingsJson` | object (any keys), default `{}` | Multivariate settings: Which combinations Mailchimp tests and how it picks the winner. — used when `type` is "variate" |
| `segmentOptionsJson` | object (any keys), default `{}` | Segment conditions: Mailchimp's own condition grammar, when a saved segment id is not enough. |

Output: `campaign` object — Campaign · `campaignId` string — Campaign id · `status` string — Status.

```json
{"type":"mailchimp.createCampaign","version":1,"label":"Create campaign","config":{"type":"regular","audienceId":"","savedSegmentId":"","subjectLine":"","previewText":"","title":"","fromName":"","replyTo":"","toName":"","templateId":"","folderId":"","contentType":"","trackOpens":"","trackHtmlClicks":"","trackTextClicks":"","googleAnalytics":"","rssFeedUrl":"","rssFrequency":"","rssScheduleJson":{},"variateSettingsJson":{},"segmentOptionsJson":{},"connection":"<connection id>"}}
```

## `mailchimp.createMergeField@1` — Mailchimp — Create merge field

Adds a merge field to an audience so contact writes can fill it.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `name` | text, default `""` | Name: What a person reading the sign-up form sees. |
| `type` | "text" or "number" or "address" or "phone" or "date" or "url" or "imageurl" or "radio" or "dropdown" or "birthday" or "zip", default `"text"` | Type |
| `tag` | text, default `""` | Merge tag: The short name a contact write is keyed by, such as FNAME. Left empty, Mailchimp derives one. |
| `required` | true/false, default `false` | Required |
| `defaultValue` | text, default `""` | Default value |
| `publicField` | true/false, default `true` | Show on the sign-up form |
| `displayOrder` | text, default `""` | Position on the form: A whole number. Left empty, Mailchimp puts it last. |
| `helpText` | text, default `""` | Help text |
| `optionsJson` | object (any keys), default `{}` | Type options: What the type needs: choices for a dropdown or radio, a date format, a phone format, a country, a size. |

Output: `mergeField` object — Merge field · `mergeFieldId` number — Merge field id · `tag` string — Merge tag.

```json
{"type":"mailchimp.createMergeField","version":1,"label":"Create merge field","config":{"audienceId":"","name":"","type":"text","tag":"","required":false,"defaultValue":"","publicField":true,"displayOrder":"","helpText":"","optionsJson":{},"connection":"<connection id>"}}
```

## `mailchimp.createSegment@1` — Mailchimp — Create segment

Creates a saved or static segment in an audience.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `name` | text, default `""` | Segment name |
| `kind` | "static" or "saved", default `"static"` | Kind |
| `staticMembers` | text, default `""` | Addresses: Comma separated. Left empty, Mailchimp creates an empty static segment you can fill later. — used when `kind` is "static" |
| `match` | "any" or "all", default `"any"` | Match — used when `kind` is "saved" |
| `conditionsJson` | list of any JSON, default `[]` | Conditions: Mailchimp's own condition grammar — around forty condition types, each with its own operators. The vendor's segmentation-conditions page lists them. — used when `kind` is "saved" |

Output: `segment` object — Segment · `segmentId` number — Segment id · `memberCount` number — Contacts in the segment.

```json
{"type":"mailchimp.createSegment","version":1,"label":"Create segment","config":{"audienceId":"","name":"","kind":"static","staticMembers":"","match":"any","conditionsJson":[],"connection":"<connection id>"}}
```

## `mailchimp.deleteCampaign@1` — Mailchimp — Delete campaign

Deletes a campaign that has not been sent.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id |

Output: `deleted` boolean — Deleted · `wasPresent` boolean — The campaign was still there · `campaignId` string — Campaign id.

```json
{"type":"mailchimp.deleteCampaign","version":1,"label":"Delete campaign","config":{"campaignId":"","connection":"<connection id>"}}
```

## `mailchimp.deleteMemberPermanently@1` — Mailchimp — Erase contact permanently

Erases a contact for good; the address can never be imported into this audience again.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address: This cannot be undone. Archive contact is the reversible one; use this only for a real erasure request. |

Output: `erased` boolean — Erased · `wasPresent` boolean — The contact was still there · `email` string — E-mail address.

```json
{"type":"mailchimp.deleteMemberPermanently","version":1,"label":"Erase contact permanently","config":{"audienceId":"","email":"","connection":"<connection id>"}}
```

## `mailchimp.deleteSegment@1` — Mailchimp — Delete segment

Deletes a segment from an audience; the contacts in it stay.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `segmentId` | text, default `""` | Segment id |

Output: `deleted` boolean — Deleted · `wasPresent` boolean — The segment was still there · `segmentId` string — Segment id.

```json
{"type":"mailchimp.deleteSegment","version":1,"label":"Delete segment","config":{"audienceId":"","segmentId":"","connection":"<connection id>"}}
```

## `mailchimp.getAccount@1` — Mailchimp — Get account

Reads the Mailchimp account's name, plan and total number of subscribers.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |

Output: `account` object — Account · `accountId` string — Account id · `accountName` string — Account name · `totalSubscribers` number — Subscribers in total.

```json
{"type":"mailchimp.getAccount","version":1,"label":"Get account","config":{"fields":"","connection":"<connection id>"}}
```

## `mailchimp.getAudience@1` — Mailchimp — Get audience

Reads one Mailchimp audience with its contact and unsubscribe counts.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id: The id List audiences gives you; it is also the id= in the Mailchimp audience URL. — a value from the vendor's own list |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |

Output: `audience` object — Audience · `memberCount` number — Contacts · `unsubscribeCount` number — Unsubscribes.

```json
{"type":"mailchimp.getAudience","version":1,"label":"Get audience","config":{"audienceId":"","fields":"","connection":"<connection id>"}}
```

## `mailchimp.getBatchStatus@1` — Mailchimp — Get batch status

Reads how far a background batch has got and how many calls failed.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `batchId` | text, default `""` | Batch id: From Start batch. The per-call results live in a compressed archive Mailchimp keeps at its own address; this node hands back that address, not its contents. |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |

Output: `batch` object — Batch · `status` string — Status · `totalOperations` number — Operations · `finishedOperations` number — Finished · `erroredOperations` number — Failed · `responseBodyUrl` string — Results archive address · `isFinished` boolean — Finished.

```json
{"type":"mailchimp.getBatchStatus","version":1,"label":"Get batch status","config":{"batchId":"","fields":"","connection":"<connection id>"}}
```

## `mailchimp.getCampaign@1` — Mailchimp — Get campaign

Reads one campaign with its settings, recipients and send time.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id: The id List campaigns gives you. |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |

Output: `campaign` object — Campaign · `status` string — Status · `subjectLine` string — Subject line · `sendTime` string — Send time.

```json
{"type":"mailchimp.getCampaign","version":1,"label":"Get campaign","config":{"campaignId":"","fields":"","connection":"<connection id>"}}
```

## `mailchimp.getCampaignContent@1` — Mailchimp — Get campaign content

Reads the HTML and plain text a campaign will send.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. A full HTML body is large; asking for plain_text alone keeps the run small. |

Output: `content` object — Content · `html` string — HTML · `plainText` string — Plain text.

```json
{"type":"mailchimp.getCampaignContent","version":1,"label":"Get campaign content","config":{"campaignId":"","fields":"","connection":"<connection id>"}}
```

## `mailchimp.getCampaignReport@1` — Mailchimp — Get campaign report

Reads one campaign's opens, clicks, bounces and unsubscribes.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |

Output: `report` object — Report · `emailsSent` number — E-mails sent · `openRate` number — Open rate · `clickRate` number — Click rate · `bounceCount` number — Bounces · `unsubscribed` number — Unsubscribes.

```json
{"type":"mailchimp.getCampaignReport","version":1,"label":"Get campaign report","config":{"campaignId":"","fields":"","connection":"<connection id>"}}
```

## `mailchimp.getMember@1` — Mailchimp — Get contact

Reads one contact in an audience with their status, tags and merge fields.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |

Output: `member` object — Contact · `status` string — Status · `tags` array — Tags · `mergeFields` object — Merge fields · `memberRating` number — Rating · `lastChanged` string — Last changed.

```json
{"type":"mailchimp.getMember","version":1,"label":"Get contact","config":{"audienceId":"","email":"","fields":"","connection":"<connection id>"}}
```

## `mailchimp.getSegment@1` — Mailchimp — Get segment

Reads one segment of an audience with its contact count.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `segmentId` | text, default `""` | Segment id: The id List segments gives you. |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |

Output: `segment` object — Segment · `segmentName` string — Segment name · `memberCount` number — Contacts in the segment.

```json
{"type":"mailchimp.getSegment","version":1,"label":"Get segment","config":{"audienceId":"","segmentId":"","fields":"","connection":"<connection id>"}}
```

## `mailchimp.getTemplate@1` — Mailchimp — Get template

Reads one e-mail template with its name, type and category.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `templateId` | text, default `""` | Template id — a value from the vendor's own list |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |

Output: `template` object — Template · `templateName` string — Template name · `type` string — Type.

```json
{"type":"mailchimp.getTemplate","version":1,"label":"Get template","config":{"templateId":"","fields":"","connection":"<connection id>"}}
```

## `mailchimp.listAudiences@2` — Mailchimp — List audiences

Lists the audiences this Mailchimp account holds.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `email` | text, default `""` | Address that is a member: Only the audiences this address is a contact of. |
| `sinceDateCreated` | text, default `""` | Created since |
| `beforeDateCreated` | text, default `""` | Created before |
| `sinceCampaignLastSent` | text, default `""` | Sent to since: Only audiences a campaign went out to after this moment. |
| `hasEcommerceStore` | "" or "true" or "false", default `""` | Has a webshop |
| `sortDir` | "" or "ASC" or "DESC", default `""` | Sort direction: Sorts on the date the audience was created. |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. On a page of 1,000 this is the difference between fitting this run's size limit and not. |
| `limit` | number, default `100` | Audiences per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many: Mailchimp pages by position, not by cursor. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. Mailchimp pages by position, so a collection that changes while the walk runs can repeat or skip a row. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `audiences` array — Audiences · `audienceCount` number — Audiences in this answer · `hasMore` boolean — More audiences are waiting behind this page · `totalItems` number — Audiences in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listAudiences","version":2,"label":"List audiences","config":{"email":"","sinceDateCreated":"","beforeDateCreated":"","sinceCampaignLastSent":"","hasEcommerceStore":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listAudiences@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `email` | text, default `""` | Address that is a member: Only the audiences this address is a contact of. |
| `sinceDateCreated` | text, default `""` | Created since |
| `beforeDateCreated` | text, default `""` | Created before |
| `sinceCampaignLastSent` | text, default `""` | Sent to since: Only audiences a campaign went out to after this moment. |
| `hasEcommerceStore` | "" or "true" or "false", default `""` | Has a webshop |
| `sortDir` | "" or "ASC" or "DESC", default `""` | Sort direction: Sorts on the date the audience was created. |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. On a page of 1,000 this is the difference between fitting this run's size limit and not. |
| `limit` | number, default `100` | Audiences per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many: Mailchimp pages by position, not by cursor. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. Mailchimp pages by position, so a collection that changes while the walk runs can repeat or skip a row. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"email":"","sinceDateCreated":"","beforeDateCreated":"","sinceCampaignLastSent":"","hasEcommerceStore":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"email":"","sinceDateCreated":"","beforeDateCreated":"","sinceCampaignLastSent":"","hasEcommerceStore":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listCampaignReports@2` — Mailchimp — List campaign reports

Lists the delivery reports of sent campaigns.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `type` | "" or "regular" or "plaintext" or "absplit" or "rss" or "variate", default `""` | Campaign type |
| `sinceSendTime` | text, default `""` | Sent since |
| `beforeSendTime` | text, default `""` | Sent before |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Reports per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `reports` array — Reports · `reportCount` number — Reports in this answer · `hasMore` boolean — More reports are waiting behind this page · `totalItems` number — Reports in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listCampaignReports","version":2,"label":"List campaign reports","config":{"type":"","sinceSendTime":"","beforeSendTime":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listCampaignReports@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `type` | "" or "regular" or "plaintext" or "absplit" or "rss" or "variate", default `""` | Campaign type |
| `sinceSendTime` | text, default `""` | Sent since |
| `beforeSendTime` | text, default `""` | Sent before |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Reports per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"type":"","sinceSendTime":"","beforeSendTime":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"type":"","sinceSendTime":"","beforeSendTime":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listCampaigns@2` — Mailchimp — List campaigns

Lists campaigns, filtered by type, status, audience or send time.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `type` | "" or "regular" or "plaintext" or "absplit" or "rss" or "variate", default `""` | Type |
| `status` | "" or "save" or "paused" or "schedule" or "sending" or "sent", default `""` | Status |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `folderId` | text, default `""` | Folder id |
| `memberId` | text, default `""` | Contact id: Only campaigns this contact was sent. |
| `sinceSendTime` | text, default `""` | Sent since |
| `beforeSendTime` | text, default `""` | Sent before |
| `sinceCreateTime` | text, default `""` | Created since |
| `beforeCreateTime` | text, default `""` | Created before |
| `sortField` | "" or "create_time" or "send_time", default `""` | Sort on |
| `sortDir` | "" or "ASC" or "DESC", default `""` | Sort direction |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Campaigns per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 campaigns rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `campaigns` array — Campaigns · `campaignCount` number — Campaigns in this answer · `hasMore` boolean — More campaigns are waiting behind this page · `totalItems` number — Campaigns in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listCampaigns","version":2,"label":"List campaigns","config":{"type":"","status":"","audienceId":"","folderId":"","memberId":"","sinceSendTime":"","beforeSendTime":"","sinceCreateTime":"","beforeCreateTime":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listCampaigns@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `type` | "" or "regular" or "plaintext" or "absplit" or "rss" or "variate", default `""` | Type |
| `status` | "" or "save" or "paused" or "schedule" or "sending" or "sent", default `""` | Status |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `folderId` | text, default `""` | Folder id |
| `memberId` | text, default `""` | Contact id: Only campaigns this contact was sent. |
| `sinceSendTime` | text, default `""` | Sent since |
| `beforeSendTime` | text, default `""` | Sent before |
| `sinceCreateTime` | text, default `""` | Created since |
| `beforeCreateTime` | text, default `""` | Created before |
| `sortField` | "" or "create_time" or "send_time", default `""` | Sort on |
| `sortDir` | "" or "ASC" or "DESC", default `""` | Sort direction |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Campaigns per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 campaigns rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"type":"","status":"","audienceId":"","folderId":"","memberId":"","sinceSendTime":"","beforeSendTime":"","sinceCreateTime":"","beforeCreateTime":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"type":"","status":"","audienceId":"","folderId":"","memberId":"","sinceSendTime":"","beforeSendTime":"","sinceCreateTime":"","beforeCreateTime":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listInterestCategories@2` — Mailchimp — List interest categories

Reads the groups an audience offers, such as Topics or Preferences.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `type` | "" or "checkboxes" or "dropdown" or "radio" or "hidden", default `""` | Only this kind: How the group is drawn on the sign-up form. |
| `sortField` | "" or "name" or "display_order", default `""` | Sort on |
| `sortDir` | "" or "ASC" or "DESC", default `""` | Sort direction |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Categories per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `categories` array — Interest categories · `categoryCount` number — Categories in this answer · `hasMore` boolean — More are waiting behind this page · `totalItems` number — Categories in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listInterestCategories","version":2,"label":"List interest categories","config":{"audienceId":"","type":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listInterestCategories@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `type` | "" or "checkboxes" or "dropdown" or "radio" or "hidden", default `""` | Only this kind: How the group is drawn on the sign-up form. |
| `sortField` | "" or "name" or "display_order", default `""` | Sort on |
| `sortDir` | "" or "ASC" or "DESC", default `""` | Sort direction |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Categories per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"audienceId":"","type":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"audienceId":"","type":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listInterests@2` — Mailchimp — List interests

Reads the choices in one group, with the ids a contact write needs.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `categoryId` | text, default `""` | Interest category id: From List interest categories. |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. The ids here are what Interest groups on a contact write is keyed by. |
| `limit` | number, default `100` | Interests per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `interests` array — Interests · `interestCount` number — Interests in this answer · `hasMore` boolean — More are waiting behind this page · `totalItems` number — Interests in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listInterests","version":2,"label":"List interests","config":{"audienceId":"","categoryId":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listInterests@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `categoryId` | text, default `""` | Interest category id: From List interest categories. |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. The ids here are what Interest groups on a contact write is keyed by. |
| `limit` | number, default `100` | Interests per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"audienceId":"","categoryId":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"audienceId":"","categoryId":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listMemberActivity@2` — Mailchimp — List contact activity

Reads a contact's opens, clicks, bounces and unsubscribes.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `activityFilters` | text, default `""` | Kinds of activity: Comma separated. Left empty, Mailchimp answers every kind. |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Rows per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `activity` array — Activity · `activityCount` number — Rows in this answer · `hasMore` boolean — More rows are waiting behind this page · `totalItems` number — Rows in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listMemberActivity","version":2,"label":"List contact activity","config":{"audienceId":"","email":"","activityFilters":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listMemberActivity@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `activityFilters` | text, default `""` | Kinds of activity: Comma separated. Left empty, Mailchimp answers every kind. |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Rows per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"audienceId":"","email":"","activityFilters":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"audienceId":"","email":"","activityFilters":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listMemberEvents@2` — Mailchimp — List contact events

Reads the custom events written onto one contact.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Events per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `events` array — Events · `eventCount` number — Events in this answer · `hasMore` boolean — More events are waiting behind this page · `totalItems` number — Events in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listMemberEvents","version":2,"label":"List contact events","config":{"audienceId":"","email":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listMemberEvents@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Events per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"audienceId":"","email":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"audienceId":"","email":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listMemberTags@2` — Mailchimp — List contact tags

Reads the tags that are on one contact.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Tags per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `tags` array — Tags · `tagCount` number — Tags in this answer · `hasMore` boolean — More tags are waiting behind this page · `totalItems` number — Tags in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listMemberTags","version":2,"label":"List contact tags","config":{"audienceId":"","email":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listMemberTags@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Tags per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"audienceId":"","email":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"audienceId":"","email":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listMembers@2` — Mailchimp — List contacts

Lists the contacts of an audience, filtered by status, date or interest.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `status` | "" or "subscribed" or "unsubscribed" or "cleaned" or "pending" or "transactional" or "archived", default `""` | Status |
| `emailType` | "" or "html" or "text", default `""` | E-mail format |
| `sinceLastChanged` | text, default `""` | Changed since |
| `beforeLastChanged` | text, default `""` | Changed before |
| `sinceTimestampOpt` | text, default `""` | Opted in since |
| `beforeTimestampOpt` | text, default `""` | Opted in before |
| `unsubscribedSince` | text, default `""` | Unsubscribed since: Mailchimp only accepts this filter together with the Unsubscribed status. |
| `sinceLastCampaign` | text, default `""` | Sent to since |
| `uniqueEmailId` | text, default `""` | Unique e-mail id: Mailchimp's own id for an address across every audience. |
| `vipOnly` | true/false, default `false` | VIPs only |
| `interestCategoryId` | text, default `""` | Interest group id: From List interest categories. Needed by the two fields below. |
| `interestIds` | text, default `""` | Interest ids: Comma separated. Mailchimp needs an interest group id beside them. |
| `interestMatch` | "" or "any" or "all" or "none", default `""` | Interest match |
| `sortField` | "" or "timestamp_opt" or "timestamp_signup" or "last_changed", default `""` | Sort on |
| `sortDir` | "" or "ASC" or "DESC", default `""` | Sort direction |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. On a page of 1,000 this is the difference between fitting this run's size limit and not. |
| `limit` | number, default `100` | Contacts per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many: Mailchimp pages by position, not by cursor. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 contacts rather than answering a short list. Mailchimp pages by position, so an audience that changes while the walk runs can repeat or skip a contact. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `members` array — Contacts · `memberCount` number — Contacts in this answer · `hasMore` boolean — More contacts are waiting behind this page · `totalItems` number — Contacts in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listMembers","version":2,"label":"List contacts","config":{"audienceId":"","status":"","emailType":"","sinceLastChanged":"","beforeLastChanged":"","sinceTimestampOpt":"","beforeTimestampOpt":"","unsubscribedSince":"","sinceLastCampaign":"","uniqueEmailId":"","vipOnly":false,"interestCategoryId":"","interestIds":"","interestMatch":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listMembers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `status` | "" or "subscribed" or "unsubscribed" or "cleaned" or "pending" or "transactional" or "archived", default `""` | Status |
| `emailType` | "" or "html" or "text", default `""` | E-mail format |
| `sinceLastChanged` | text, default `""` | Changed since |
| `beforeLastChanged` | text, default `""` | Changed before |
| `sinceTimestampOpt` | text, default `""` | Opted in since |
| `beforeTimestampOpt` | text, default `""` | Opted in before |
| `unsubscribedSince` | text, default `""` | Unsubscribed since: Mailchimp only accepts this filter together with the Unsubscribed status. |
| `sinceLastCampaign` | text, default `""` | Sent to since |
| `uniqueEmailId` | text, default `""` | Unique e-mail id: Mailchimp's own id for an address across every audience. |
| `vipOnly` | true/false, default `false` | VIPs only |
| `interestCategoryId` | text, default `""` | Interest group id: From List interest categories. Needed by the two fields below. |
| `interestIds` | text, default `""` | Interest ids: Comma separated. Mailchimp needs an interest group id beside them. |
| `interestMatch` | "" or "any" or "all" or "none", default `""` | Interest match |
| `sortField` | "" or "timestamp_opt" or "timestamp_signup" or "last_changed", default `""` | Sort on |
| `sortDir` | "" or "ASC" or "DESC", default `""` | Sort direction |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. On a page of 1,000 this is the difference between fitting this run's size limit and not. |
| `limit` | number, default `100` | Contacts per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many: Mailchimp pages by position, not by cursor. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 contacts rather than answering a short list. Mailchimp pages by position, so an audience that changes while the walk runs can repeat or skip a contact. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"audienceId":"","status":"","emailType":"","sinceLastChanged":"","beforeLastChanged":"","sinceTimestampOpt":"","beforeTimestampOpt":"","unsubscribedSince":"","sinceLastCampaign":"","uniqueEmailId":"","vipOnly":false,"interestCategoryId":"","interestIds":"","interestMatch":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"audienceId":"","status":"","emailType":"","sinceLastChanged":"","beforeLastChanged":"","sinceTimestampOpt":"","beforeTimestampOpt":"","unsubscribedSince":"","sinceLastCampaign":"","uniqueEmailId":"","vipOnly":false,"interestCategoryId":"","interestIds":"","interestMatch":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listMergeFields@2` — Mailchimp — List merge fields

Reads the merge tags an audience defines, such as FNAME and LNAME.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `type` | "" or "text" or "number" or "address" or "phone" or "date" or "url" or "imageurl" or "radio" or "dropdown" or "birthday" or "zip", default `""` | Only this type |
| `required` | "" or "true" or "false", default `""` | Only required ones |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Merge fields per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `mergeFields` array — Merge fields · `mergeFieldCount` number — Merge fields in this answer · `hasMore` boolean — More are waiting behind this page · `totalItems` number — Merge fields in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listMergeFields","version":2,"label":"List merge fields","config":{"audienceId":"","type":"","required":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listMergeFields@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `type` | "" or "text" or "number" or "address" or "phone" or "date" or "url" or "imageurl" or "radio" or "dropdown" or "birthday" or "zip", default `""` | Only this type |
| `required` | "" or "true" or "false", default `""` | Only required ones |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Merge fields per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"audienceId":"","type":"","required":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"audienceId":"","type":"","required":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listSegments@2` — Mailchimp — List segments

Lists an audience's segments, and the tags stored as static segments.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `type` | "" or "saved" or "static" or "fuzzy", default `""` | Only this kind |
| `excludeType` | "" or "saved" or "static" or "fuzzy", default `""` | Leave out this kind: A tag is a static segment in Mailchimp's data model, so leaving static out here is how you list saved segments only. |
| `includeCleaned` | true/false, default `false` | Count cleaned contacts |
| `includeTransactional` | true/false, default `false` | Count transactional contacts |
| `includeUnsubscribed` | true/false, default `false` | Count unsubscribed contacts |
| `sinceCreatedAt` | text, default `""` | Created since |
| `beforeCreatedAt` | text, default `""` | Created before |
| `sinceUpdatedAt` | text, default `""` | Changed since |
| `beforeUpdatedAt` | text, default `""` | Changed before |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Segments per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `segments` array — Segments · `segmentCount` number — Segments in this answer · `hasMore` boolean — More segments are waiting behind this page · `totalItems` number — Segments in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listSegments","version":2,"label":"List segments","config":{"audienceId":"","type":"","excludeType":"","includeCleaned":false,"includeTransactional":false,"includeUnsubscribed":false,"sinceCreatedAt":"","beforeCreatedAt":"","sinceUpdatedAt":"","beforeUpdatedAt":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listSegments@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `type` | "" or "saved" or "static" or "fuzzy", default `""` | Only this kind |
| `excludeType` | "" or "saved" or "static" or "fuzzy", default `""` | Leave out this kind: A tag is a static segment in Mailchimp's data model, so leaving static out here is how you list saved segments only. |
| `includeCleaned` | true/false, default `false` | Count cleaned contacts |
| `includeTransactional` | true/false, default `false` | Count transactional contacts |
| `includeUnsubscribed` | true/false, default `false` | Count unsubscribed contacts |
| `sinceCreatedAt` | text, default `""` | Created since |
| `beforeCreatedAt` | text, default `""` | Created before |
| `sinceUpdatedAt` | text, default `""` | Changed since |
| `beforeUpdatedAt` | text, default `""` | Changed before |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Segments per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"audienceId":"","type":"","excludeType":"","includeCleaned":false,"includeTransactional":false,"includeUnsubscribed":false,"sinceCreatedAt":"","beforeCreatedAt":"","sinceUpdatedAt":"","beforeUpdatedAt":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"audienceId":"","type":"","excludeType":"","includeCleaned":false,"includeTransactional":false,"includeUnsubscribed":false,"sinceCreatedAt":"","beforeCreatedAt":"","sinceUpdatedAt":"","beforeUpdatedAt":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.listTags@1` — Mailchimp — List tags

Lists an audience's tags, optionally only those starting with a name.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `name` | text, default `""` | Name starts with: Left empty, Mailchimp answers every tag the audience has. |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |

Output: `tags` array — Tags · `tagCount` number — Tags found · `totalItems` number — Tags Mailchimp counted.

```json
{"type":"mailchimp.listTags","version":1,"label":"List tags","config":{"audienceId":"","name":"","fields":"","connection":"<connection id>"}}
```

## `mailchimp.listTemplates@2` — Mailchimp — List templates

Lists the e-mail templates the account can build a campaign from.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `createdBy` | text, default `""` | Created by |
| `type` | text, default `""` | Type: Mailchimp's own template types, such as user, base or gallery. |
| `category` | text, default `""` | Category |
| `folderId` | text, default `""` | Folder id |
| `contentType` | "" or "html" or "template" or "multichannel", default `""` | Content type |
| `sinceDateCreated` | text, default `""` | Created since |
| `beforeDateCreated` | text, default `""` | Created before |
| `sortField` | "" or "date_created" or "date_edited" or "name", default `""` | Sort on |
| `sortDir` | "" or "ASC" or "DESC", default `""` | Sort direction |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Templates per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `templates` array — Templates · `templateCount` number — Templates in this answer · `hasMore` boolean — More templates are waiting behind this page · `totalItems` number — Templates in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"mailchimp.listTemplates","version":2,"label":"List templates","config":{"createdBy":"","type":"","category":"","folderId":"","contentType":"","sinceDateCreated":"","beforeDateCreated":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `mailchimp.listTemplates@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `createdBy` | text, default `""` | Created by |
| `type` | text, default `""` | Type: Mailchimp's own template types, such as user, base or gallery. |
| `category` | text, default `""` | Category |
| `folderId` | text, default `""` | Folder id |
| `contentType` | "" or "html" or "template" or "multichannel", default `""` | Content type |
| `sinceDateCreated` | text, default `""` | Created since |
| `beforeDateCreated` | text, default `""` | Created before |
| `sortField` | "" or "date_created" or "date_edited" or "name", default `""` | Sort on |
| `sortDir` | "" or "ASC" or "DESC", default `""` | Sort direction |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |
| `limit` | number, default `100` | Templates per call: 1 to 1,000. |
| `offset` | number, default `0` | Skip this many |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Mailchimp's pages, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"createdBy":"","type":"","category":"","folderId":"","contentType":"","sinceDateCreated":"","beforeDateCreated":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"createdBy":"","type":"","category":"","folderId":"","contentType":"","sinceDateCreated":"","beforeDateCreated":"","sortField":"","sortDir":"","fields":"","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `mailchimp.replicateCampaign@1` — Mailchimp — Replicate campaign

Copies a campaign into a new draft with the same settings and content.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id: Mailchimp copies saved and sent campaigns. |

Output: `campaign` object — The copy · `campaignId` string — New campaign id.

```json
{"type":"mailchimp.replicateCampaign","version":1,"label":"Replicate campaign","config":{"campaignId":"","connection":"<connection id>"}}
```

## `mailchimp.scheduleCampaign@1` — Mailchimp — Schedule campaign

Sets the UTC quarter hour at which a campaign goes out.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id |
| `scheduleTime` | text, default `""` | Send at: UTC, and on the quarter hour: minute 00, 15, 30 or 45. Anything else is refused here. |
| `timewarp` | true/false, default `false` | Send in each contact's own time zone: Mailchimp does not allow this together with batch delivery. |
| `batchDelay` | text, default `""` | Minutes between batches |
| `batchCount` | text, default `""` | Number of batches |

Output: `scheduled` boolean — Scheduled · `scheduleTime` string — Send time · `campaignId` string — Campaign id.

```json
{"type":"mailchimp.scheduleCampaign","version":1,"label":"Schedule campaign","config":{"campaignId":"","scheduleTime":"","timewarp":false,"batchDelay":"","batchCount":"","connection":"<connection id>"}}
```

## `mailchimp.searchMembers@1` — Mailchimp — Search contacts

Finds contacts across every audience by address or name.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `query` | text, default `""` | Search for: A whole address lands in the exact matches; a name or a fragment lands in the wider list. |
| `audienceId` | text, default `""` | Audience id: Left empty, Mailchimp searches every audience in the account. — a value from the vendor's own list |
| `fields` | text, default `""` | Fields to fetch: Mailchimp returns everything unless you name the fields you want. |

Output: `exactMatches` array — Exact matches · `exactMatchCount` number — Exact matches found · `fullSearch` array — Wider matches · `fullSearchCount` number — Wider matches found.

```json
{"type":"mailchimp.searchMembers","version":1,"label":"Search contacts","config":{"query":"","audienceId":"","fields":"","connection":"<connection id>"}}
```

## `mailchimp.sendCampaign@1` — Mailchimp — Send campaign

Sends a campaign to its whole audience now; it cannot be unsent.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id: An RSS campaign ignores this and goes out on its own schedule. A campaign without a reply-to address is refused. |

Output: `sent` boolean — Handed to Mailchimp · `campaignId` string — Campaign id.

```json
{"type":"mailchimp.sendCampaign","version":1,"label":"Send campaign","config":{"campaignId":"","connection":"<connection id>"}}
```

## `mailchimp.sendTestEmail@1` — Mailchimp — Send test e-mail

Sends a preview of a campaign to a few addresses of your own.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id |
| `testEmails` | text, default `""` | Send to: Your own addresses — this really sends mail. |
| `sendType` | "html" or "plaintext", default `"html"` | Format |

Output: `sent` boolean — Test handed to Mailchimp · `recipientCount` number — Addresses · `campaignId` string — Campaign id.

```json
{"type":"mailchimp.sendTestEmail","version":1,"label":"Send test e-mail","config":{"campaignId":"","testEmails":"","sendType":"html","connection":"<connection id>"}}
```

## `mailchimp.setCampaignContent@1` — Mailchimp — Set campaign content

Replaces what a campaign sends with HTML, a web page, a template or an archive.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id |
| `html` | text, default `""` | HTML: One source only: HTML, a web address, a template or an archive. |
| `plainText` | text, default `""` | Plain text: Sent beside any of the sources. Left empty, Mailchimp derives it. |
| `url` | text, default `""` | Import from a web address: Mailchimp fetches the page and uses it as the content. |
| `templateId` | text, default `""` | Template id — a value from the vendor's own list |
| `templateSections` | object of text, default `{}` | Template sections: Keyed by the section name in the template's own mc:edit attributes. |
| `archiveBase64` | text, default `""` | Archive: The archive's bytes as base64. Mailchimp takes no multipart upload here. |
| `archiveType` | "" or "zip" or "tar.gz" or "tar.bz2" or "tar" or "tgz" or "tbz", default `""` | Archive type |

Output: `content` object — Content · `plainText` string — Plain text · `campaignId` string — Campaign id.

```json
{"type":"mailchimp.setCampaignContent","version":1,"label":"Set campaign content","config":{"campaignId":"","html":"","plainText":"","url":"","templateId":"","templateSections":{},"archiveBase64":"","archiveType":"","connection":"<connection id>"}}
```

## `mailchimp.setMemberTags@1` — Mailchimp — Set contact tags

Sets which tags a contact carries, creating a tag Mailchimp does not know yet.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `tags` | list of {name?: text, status?: "active" or "inactive"}, default `[]` | Tags: One row per tag. Active adds it, Inactive takes it off. A tag Mailchimp does not know yet is created when you set it active. |
| `tagsJson` | list of any JSON, default `[]` | Tags from an earlier node: The rows above are literal text. Use this instead when the tag names come from an earlier node; it replaces them. |
| `isSyncing` | true/false, default `false` | Do not fire automations: Mailchimp's own automations that watch these tags will not fire. |

Output: `tagged` boolean — Tags were set · `tagCount` number — Tags in this call · `email` string — E-mail address.

```json
{"type":"mailchimp.setMemberTags","version":1,"label":"Set contact tags","config":{"audienceId":"","email":"","tags":[],"tagsJson":[],"isSyncing":false,"connection":"<connection id>"}}
```

## `mailchimp.setStaticSegmentMembers@1` — Mailchimp — Change static segment members

Adds and removes addresses in a static segment in one call.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `segmentId` | text, default `""` | Segment id |
| `membersToAdd` | text, default `""` | Addresses to add: Comma separated, at most 500. An address that is not on the audience is ignored by Mailchimp. |
| `membersToRemove` | text, default `""` | Addresses to remove: Comma separated, at most 500. They stay in the audience. |

Output: `membersAdded` array — Addresses added · `addedCount` number — Added · `errors` array — Addresses Mailchimp refused · `errorCount` number — Refused.

```json
{"type":"mailchimp.setStaticSegmentMembers","version":1,"label":"Change static segment members","config":{"audienceId":"","segmentId":"","membersToAdd":"","membersToRemove":"","connection":"<connection id>"}}
```

## `mailchimp.startBatch@1` — Mailchimp — Start batch

Runs up to 500 Mailchimp calls as one background job.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `operations` | list of any JSON, default `[]` | Operations: A list of calls, at most 500. Each one has a method, a path that starts with a slash, and optionally params, a body and your own operation id. |

Output: `batch` object — Batch · `batchId` string — Batch id · `status` string — Status · `totalOperations` number — Operations.

```json
{"type":"mailchimp.startBatch","version":1,"label":"Start batch","config":{"operations":[],"connection":"<connection id>"}}
```

## `mailchimp.triggerJourneyStep@1` — Mailchimp — Start journey step

Enters one contact into a Customer Journey at the step you name.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `journeyId` | text, default `""` | Journey id: Add a Customer Journey API starting point or step in the Mailchimp journey builder; Mailchimp then prints the URL that contains both ids. |
| `stepId` | text, default `""` | Step id |
| `email` | text, default `""` | E-mail address |

Output: `triggered` boolean — Entered the journey · `email` string — E-mail address · `journeyId` number — Journey id · `stepId` number — Step id.

```json
{"type":"mailchimp.triggerJourneyStep","version":1,"label":"Start journey step","config":{"journeyId":"","stepId":"","email":"","connection":"<connection id>"}}
```

## `mailchimp.unscheduleCampaign@1` — Mailchimp — Unschedule campaign

Takes a campaign off its send time and puts it back in draft.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id: Only works before the send has started. |

Output: `unscheduled` boolean — Unscheduled · `campaignId` string — Campaign id.

```json
{"type":"mailchimp.unscheduleCampaign","version":1,"label":"Unschedule campaign","config":{"campaignId":"","connection":"<connection id>"}}
```

## `mailchimp.updateCampaign@1` — Mailchimp — Update campaign

Changes a campaign's subject line, sender and other settings.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `campaignId` | text, default `""` | Campaign id |
| `subjectLine` | text, default `""` | Subject line: Mailchimp wants all three of subject line, from name and reply-to on every change, even the ones you are not changing. |
| `fromName` | text, default `""` | From name |
| `replyTo` | text, default `""` | Reply-to address |
| `previewText` | text, default `""` | Preview text |
| `title` | text, default `""` | Internal name |
| `toName` | text, default `""` | To name |
| `folderId` | text, default `""` | Folder id |
| `audienceId` | text, default `""` | Audience id: Only to move the campaign to another audience. — a value from the vendor's own list |
| `savedSegmentId` | text, default `""` | Saved segment id |

Output: `campaign` object — Campaign · `campaignId` string — Campaign id · `status` string — Status.

```json
{"type":"mailchimp.updateCampaign","version":1,"label":"Update campaign","config":{"campaignId":"","subjectLine":"","fromName":"","replyTo":"","previewText":"","title":"","toName":"","folderId":"","audienceId":"","savedSegmentId":"","connection":"<connection id>"}}
```

## `mailchimp.updateMember@1` — Mailchimp — Update contact

Changes the details of a contact who is already in an audience.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address: The contact to change. Mailchimp finds them by this address. |
| `status` | "" or "subscribed" or "unsubscribed" or "pending" or "cleaned", default `""` | Status: Transactional is not offered here: Mailchimp accepts it only when the contact is created. |
| `emailType` | "" or "html" or "text", default `""` | E-mail format |
| `mergeFields` | object of text, default `{}` | Merge fields: Keyed by the merge TAG. A tag you leave out is left alone. |
| `interests` | object of true/false, default `{}` | Interest groups: Keyed by the interest id. An interest you leave out is left alone. |
| `language` | text, default `""` | Language |
| `vip` | "" or "on" or "off", default `""` | VIP |
| `latitude` | text, default `""` | Latitude |
| `longitude` | text, default `""` | Longitude |
| `marketingPermissions` | list of any JSON, default `[]` | Marketing permissions: One object per permission; the audience defines the ids. |
| `ipSignup` | text, default `""` | Sign-up IP address |
| `timestampSignup` | text, default `""` | Sign-up moment |
| `ipOpt` | text, default `""` | Opt-in IP address |
| `timestampOpt` | text, default `""` | Opt-in moment |
| `skipMergeValidation` | true/false, default `false` | Accept a missing merge field: Accepts the change even when a required merge field is missing. |

Output: `member` object — Contact · `memberId` string — Contact id · `email` string — E-mail address · `status` string — Status · `tags` array — Tags · `mergeFields` object — Merge fields.

```json
{"type":"mailchimp.updateMember","version":1,"label":"Update contact","config":{"audienceId":"","email":"","status":"","emailType":"","mergeFields":{},"interests":{},"language":"","vip":"","latitude":"","longitude":"","marketingPermissions":[],"ipSignup":"","timestampSignup":"","ipOpt":"","timestampOpt":"","skipMergeValidation":false,"connection":"<connection id>"}}
```

## `mailchimp.updateSegment@1` — Mailchimp — Update segment

Renames a segment or replaces the conditions it selects on.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id — a value from the vendor's own list |
| `segmentId` | text, default `""` | Segment id |
| `name` | text, default `""` | Segment name: Mailchimp requires a name on every change, even an unchanged one. |
| `staticMembers` | text, default `""` | Addresses: Static segments only. Comma separated, and it REPLACES the membership. Change static segment members adds and removes instead. |
| `match` | "" or "any" or "all", default `""` | Match |
| `conditionsJson` | list of any JSON, default `[]` | Conditions: Saved segments only. Replaces every condition the segment has. |

Output: `segment` object — Segment · `segmentName` string — Segment name · `memberCount` number — Contacts in the segment.

```json
{"type":"mailchimp.updateSegment","version":1,"label":"Update segment","config":{"audienceId":"","segmentId":"","name":"","staticMembers":"","match":"","conditionsJson":[],"connection":"<connection id>"}}
```

## `mailchimp.upsertMember@1` — Mailchimp — Add or update contact

Adds a contact to an audience, or updates the one already there.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp`) |
| `audienceId` | text, default `""` | Audience id: The id List audiences gives you, and the id= in the Mailchimp audience URL. — a value from the vendor's own list |
| `email` | text, default `""` | E-mail address |
| `statusIfNew` | "subscribed" or "unsubscribed" or "pending" or "transactional" or "cleaned", default `"subscribed"` | Status for a new contact: Only used when the contact is new. An existing contact keeps the status it has unless you set Status below. |
| `status` | "" or "subscribed" or "unsubscribed" or "pending" or "transactional" or "cleaned", default `""` | Status: Mailchimp will not re-subscribe a contact who unsubscribed themselves; they have to opt in again. |
| `emailType` | "" or "html" or "text", default `""` | E-mail format |
| `mergeFields` | object of text, default `{}` | Merge fields: Keyed by the merge TAG — FNAME, not First name. The List merge fields node gives you the tags. |
| `interests` | object of true/false, default `{}` | Interest groups: Keyed by the interest id, not its name — the List interests node gives you the ids. An interest you leave out is left alone. |
| `language` | text, default `""` | Language |
| `vip` | "" or "on" or "off", default `""` | VIP |
| `latitude` | text, default `""` | Latitude |
| `longitude` | text, default `""` | Longitude |
| `marketingPermissions` | list of any JSON, default `[]` | Marketing permissions: One object per permission, as Mailchimp's GDPR fields are keyed. The audience defines the ids. |
| `ipSignup` | text, default `""` | Sign-up IP address |
| `timestampSignup` | text, default `""` | Sign-up moment |
| `ipOpt` | text, default `""` | Opt-in IP address |
| `timestampOpt` | text, default `""` | Opt-in moment |
| `skipMergeValidation` | true/false, default `false` | Accept a missing merge field: Accepts the contact even when a required merge field is missing. |

Output: `member` object — Contact · `memberId` string — Contact id · `email` string — E-mail address · `status` string — Status · `tags` array — Tags · `mergeFields` object — Merge fields.

```json
{"type":"mailchimp.upsertMember","version":1,"label":"Add or update contact","config":{"audienceId":"","email":"","statusIfNew":"subscribed","status":"","emailType":"","mergeFields":{},"interests":{},"language":"","vip":"","latitude":"","longitude":"","marketingPermissions":[],"ipSignup":"","timestampSignup":"","ipOpt":"","timestampOpt":"","skipMergeValidation":false,"connection":"<connection id>"}}
```


---

## File: references/nodes/mandrill.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Mailchimp Transactional nodes (`mandrill.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `mandrill.addSuppression@1` — Mailchimp Transactional — Block an address

Puts an address on the denylist, or on the always-deliver list.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `list` | "denylist" or "allowlist", default `"denylist"` | Which list |
| `email` | text, required | Address |
| `comment` | text, default `""` | Note: Stored with the entry so a colleague can see why it is there. |
| `subaccount` | text, default `""` | Subaccount id: Blocks the address for one subaccount only. The allowlist takes none. — used when `list` is "denylist" |

Output: `entry` object — What Mailchimp stored · `email` string — Address · `added` boolean — It was not on the list yet.

```json
{"type":"mandrill.addSuppression","version":1,"label":"Block an address","config":{"list":"denylist","comment":"","subaccount":"","connection":"<connection id>","email":"<address>"}}
```

## `mandrill.cancelScheduled@1` — Mailchimp Transactional — Cancel scheduled message

Stops a scheduled message before it goes out.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `messageId` | text, required | Message id: The _id the send answered with. A false in Was still scheduled means the message is no longer scheduled: it was cancelled already, it has gone out, or the id is wrong. |

Output: `message` object — The cancelled message, empty when there was none · `wasScheduled` boolean — Was still scheduled.

```json
{"type":"mandrill.cancelScheduled","version":1,"label":"Cancel scheduled message","config":{"connection":"<connection id>","messageId":"<message id>"}}
```

## `mandrill.createTemplate@1` — Mailchimp Transactional — Create template

Adds a new template to the account under a name you choose.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `name` | text, required | Template name: Must be unique on the account — Mailchimp refuses a second template by the same name. The slug the send nodes use is derived from it and never changes afterwards. |
| `subject` | text, default `""` | Default subject |
| `fromEmail` | text, default `""` | Default from address |
| `fromName` | text, default `""` | Default from name |
| `code` | text, default `""` | HTML: Mark the editable parts with mc:edit="name" — those names are the Editable regions the send node fills in. |
| `text` | text, default `""` | Plain text |
| `labels` | text, default `""` | Labels: Comma-separated, at most 10. |
| `publish` | true/false, default `true` | Publish it straight away: Off saves the draft only, and the send nodes keep using the published version. |

Output: `template` object — The new template · `templateSlug` string — Slug.

```json
{"type":"mandrill.createTemplate","version":1,"label":"Create template","config":{"subject":"","fromEmail":"","fromName":"","code":"","text":"","labels":"","publish":true,"connection":"<connection id>","name":"<template name>"}}
```

## `mandrill.deleteTemplate@1` — Mailchimp Transactional — Delete template

Removes a template from the account for good.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `templateSlug` | text, required | Template slug: The template's immutable slug. A false in Was there means nothing by that slug existed — it was already deleted, or the slug is wrong. |

Output: `template` object — The deleted template, empty when there was none · `wasPresent` boolean — Was there.

```json
{"type":"mandrill.deleteTemplate","version":1,"label":"Delete template","config":{"connection":"<connection id>","templateSlug":"<template slug>"}}
```

## `mandrill.getAccountInfo@1` — Mailchimp Transactional — Get account

Reads this account's hourly quota, backlog and reputation.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |

Output: `account` object — The whole account record · `hourlyQuota` number — Messages allowed this hour · `backlog` number — Messages still waiting to go out · `reputation` number — Sending reputation, 0 to 100.

```json
{"type":"mandrill.getAccountInfo","version":1,"label":"Get account","config":{"connection":"<connection id>"}}
```

## `mandrill.getMessage@1` — Mailchimp Transactional — Get message

Looks up one sent message and answers its delivery state and events.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `messageId` | text, required | Message id: The _id a send, a search or a webhook answered with. |

Output: `message` object — The whole message record · `state` string — Delivery state · `email` string — Recipient · `subject` string — Subject · `opens` number — Times opened · `clicks` number — Times clicked · `smtpEvents` array — What the receiving server said · `bounceDescription` string — Why it bounced.

```json
{"type":"mandrill.getMessage","version":1,"label":"Get message","config":{"connection":"<connection id>","messageId":"<message id>"}}
```

## `mandrill.getMessageContent@1` — Mailchimp Transactional — Get message content

Reads back the full content of one recently sent message.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `messageId` | text, required | Message id: The _id a send, a search or a webhook answered with. Only recently sent mail can be read back, and a refused message has no stored content. |

Output: `content` object — Sender, subject, headers, both bodies and the attachments.

```json
{"type":"mandrill.getMessageContent","version":1,"label":"Get message content","config":{"connection":"<connection id>","messageId":"<message id>"}}
```

## `mandrill.getSubaccount@1` — Mailchimp Transactional — Get subaccount

Reads one subaccount's quota, backlog and recent sending.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `subaccountId` | text, required | Subaccount id: The List subaccounts node gives you the ids. |

Output: `subaccount` object — The subaccount.

```json
{"type":"mandrill.getSubaccount","version":1,"label":"Get subaccount","config":{"connection":"<connection id>","subaccountId":"<subaccount id>"}}
```

## `mandrill.getTemplate@1` — Mailchimp Transactional — Get template

Reads one template's draft and published versions side by side.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `templateSlug` | text, required | Template slug: The template's immutable slug, not its display name — the List templates node answers both. |

Output: `template` object — The template.

```json
{"type":"mandrill.getTemplate","version":1,"label":"Get template","config":{"connection":"<connection id>","templateSlug":"<template slug>"}}
```

## `mandrill.listScheduled@1` — Mailchimp Transactional — List scheduled messages

Shows the messages that are still waiting to be sent.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `to` | text, default `""` | Only for this address: Leave empty for everything the account still has scheduled. |

Output: `scheduled` array — Scheduled messages · `scheduledCount` number — Scheduled messages in this answer.

```json
{"type":"mandrill.listScheduled","version":1,"label":"List scheduled messages","config":{"to":"","connection":"<connection id>"}}
```

## `mandrill.listSubaccounts@1` — Mailchimp Transactional — List subaccounts

Shows the subaccounts on this account with their sending state.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `prefix` | text, default `""` | Id or name starts with: Leave empty for every subaccount on the account. |

Output: `subaccounts` array — Subaccounts · `subaccountCount` number — Subaccounts in this answer.

```json
{"type":"mandrill.listSubaccounts","version":1,"label":"List subaccounts","config":{"prefix":"","connection":"<connection id>"}}
```

## `mandrill.listSuppressions@1` — Mailchimp Transactional — List blocked addresses

Shows the addresses Mailchimp refuses, or the ones it always delivers to.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `list` | "denylist" or "allowlist", default `"denylist"` | Which list: After a hard bounce Mailchimp denylists an address and every later send to it is refused. The allowlist is the opposite: mail to those addresses is always attempted. |
| `email` | text, default `""` | Only this address: Leave empty for the whole list. On the allowlist this matches on a prefix. |
| `includeExpired` | true/false, default `false` | Include expired entries: A denylist entry expires on its own; the allowlist has no such thing. — used when `list` is "denylist" |
| `subaccount` | text, default `""` | Subaccount id: Restricts the answer to one subaccount's denylist. The allowlist endpoint takes no subaccount. — used when `list` is "denylist" |

Output: `entries` array — Addresses on the list · `entryCount` number — Addresses in this answer · `list` string — Which list was read.

```json
{"type":"mandrill.listSuppressions","version":1,"label":"List blocked addresses","config":{"list":"denylist","email":"","includeExpired":false,"subaccount":"","connection":"<connection id>"}}
```

## `mandrill.listTags@1` — Mailchimp Transactional — List tags

Shows every tag with its delivery counters and reputation.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |

Output: `tags` array — Tags · `tagCount` number — Tags in this answer.

```json
{"type":"mandrill.listTags","version":1,"label":"List tags","config":{"connection":"<connection id>"}}
```

## `mandrill.listTemplates@1` — Mailchimp Transactional — List templates

Shows every template on the account with its slug and display name.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `label` | text, default `""` | Only with this label: Leave empty for every template on the account. |

Output: `templates` array — Templates · `templateCount` number — Templates in this answer.

```json
{"type":"mandrill.listTemplates","version":1,"label":"List templates","config":{"label":"","connection":"<connection id>"}}
```

## `mandrill.removeSuppression@1` — Mailchimp Transactional — Unblock an address

Takes an address off the denylist, so mail to it is tried again.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `list` | "denylist" or "allowlist", default `"denylist"` | Which list |
| `email` | text, required | Address: A false in Was on the list means nothing was there to remove — it had already been taken off, or it never was on. |
| `subaccount` | text, default `""` | Subaccount id: Removes it from one subaccount's denylist. The allowlist takes none. — used when `list` is "denylist" |

Output: `entry` object — What Mailchimp answered, empty when there was none · `email` string — Address · `deleted` boolean — Mailchimp removed it · `wasPresent` boolean — Was on the list.

```json
{"type":"mandrill.removeSuppression","version":1,"label":"Unblock an address","config":{"list":"denylist","subaccount":"","connection":"<connection id>","email":"<address>"}}
```

## `mandrill.renderTemplate@1` — Mailchimp Transactional — Render template

Builds the HTML a template produces, without sending anything.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `templateSlug` | text, required | Template slug: The template's immutable slug — the List templates node answers it. |
| `templateContent` | object of text, default `{}` | Editable regions: One row per mc:edit region: the key is the region's name, the value the HTML that goes into it. |
| `mergeVars` | object of text, default `{}` | Merge values: *\|NAME\|* in the template is replaced by the value here. |

Output: `html` string — The rendered HTML.

```json
{"type":"mandrill.renderTemplate","version":1,"label":"Render template","config":{"templateContent":{},"mergeVars":{},"connection":"<connection id>","templateSlug":"<template slug>"}}
```

## `mandrill.rescheduleMessage@1` — Mailchimp Transactional — Reschedule message

Moves a scheduled message to another time.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `messageId` | text, required | Message id: The _id the send answered with; it must still be scheduled. |
| `sendAt` | text, required | New send time: UTC, as 2026-10-01 09:00:00, at most a year ahead. Mailchimp cannot time travel: a time in the past sends the message immediately. |

Output: `message` object — The rescheduled message.

```json
{"type":"mandrill.rescheduleMessage","version":1,"label":"Reschedule message","config":{"connection":"<connection id>","messageId":"<message id>","sendAt":"<new send time>"}}
```

## `mandrill.searchMessages@1` — Mailchimp Transactional — Search messages

Finds sent messages by address, date, tag or sender.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `query` | text, default `""` | Search: Mailchimp's own search syntax — an address on its own works, and so does email:jane@example.com or subject:invoice. |
| `dateFrom` | text, default `""` | From date |
| `dateTo` | text, default `""` | To date |
| `tags` | text, default `""` | Tags: Comma-separated. Only messages carrying one of these tags are returned. |
| `senders` | text, default `""` | Senders: Comma-separated from-addresses. Only messages sent from one of these count. |
| `limit` | number, default `100` | Messages at most: 1 to 1000. Mailchimp has no paging at all, so a search that finds more is cut off here without saying so — narrow the dates or the query instead of raising this. |

Output: `messages` array — Messages · `messageCount` number — Messages in this answer.

```json
{"type":"mandrill.searchMessages","version":1,"label":"Search messages","config":{"query":"","dateFrom":"","dateTo":"","tags":"","senders":"","limit":100,"connection":"<connection id>"}}
```

## `mandrill.sendEmail@1` — Mailchimp Transactional — Send email

Sends one transactional email with a subject and a body you compose.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `fromEmail` | text, required | From address: Must be on a domain you verified in Mailchimp Transactional — mail from an unverified domain comes back refused with the reason unsigned. |
| `fromName` | text, default `""` | From name |
| `to` | text, default `""` | To: Comma-separated. For a list from an earlier node, write {{ join(trigger.recipients, ", ") }}. |
| `cc` | text, default `""` | Cc |
| `bcc` | text, default `""` | Bcc |
| `subject` | text, default `""` | Subject |
| `attachments` | object of text, default `{}` | Attachments: The key is the file name, the value its base64 — Pick file… fills both in. The type is taken from the file's extension; use Attachments (JSON) below to set it yourself. Everything together must stay under 25 MB, and base64 is about a third larger than the file on disk. |
| `images` | object of text, default `{}` | Inline images: The key is the content id you reference from the HTML as <img src="cid:the-key">. Only image files: Mailchimp refuses an inline attachment whose type does not start with image/. |
| `globalMergeVars` | object of text, default `{}` | Merge values: *\|NAME\|* in the body is replaced by the value here. A name may not start with an underscore. |
| `mergeLanguage` | "mailchimp" or "handlebars", default `"mailchimp"` | Merge language: In Handlebars mode the braces belong to Mailchimp, so a Flowmage expression in the body has to be written with the Fixed/Expression switch rather than inline. |
| `tags` | text, default `""` | Tags: Comma-separated. Each tag is at most 50 characters and may not start with an underscore; Mailchimp stores only the first 100 distinct tags it sees. |
| `metadata` | object of text, default `{}` | Metadata: Stored with the message and searchable afterwards. Up to 10 fields can be indexed for search in Mailchimp's own settings. |
| `headers` | object of text, default `{}` | Extra headers |
| `subaccount` | text, default `""` | Subaccount id: Routes the send through a subaccount, which must already exist. |
| `trackOpens` | "" or "on" or "off", default `""` | Track opens |
| `trackClicks` | "" or "on" or "off", default `""` | Track clicks |
| `preserveRecipients` | "" or "on" or "off", default `""` | Show every recipient in To |
| `important` | "" or "on" or "off", default `""` | Send ahead of other mail |
| `autoText` | "" or "on" or "off", default `""` | Generate a plain-text part |
| `autoHtml` | "" or "on" or "off", default `""` | Generate an HTML part |
| `inlineCss` | "" or "on" or "off", default `""` | Inline the CSS: Mailchimp only inlines CSS on HTML under 256 KB. |
| `urlStripQs` | "" or "on" or "off", default `""` | Strip query strings from tracked links |
| `viewContentLink` | "" or "on" or "off", default `""` | Log the content: Off keeps the message body out of Mailchimp's own activity log, where the Get message content node reads it from. |
| `bccAddress` | text, default `""` | Bcc every message to |
| `trackingDomain` | text, default `""` | Tracking domain: A tracking domain already set up on the account; empty uses the account's own. |
| `returnPathDomain` | text, default `""` | Return-path domain: Signing domain is deliberately not offered here: Mailchimp ignores it for every account created on or after 1 December 2015. |
| `googleAnalyticsDomains` | text, default `""` | Google Analytics domains: Comma-separated. Links to these domains get the campaign parameters added. |
| `googleAnalyticsCampaign` | text, default `""` | Google Analytics campaign |
| `recipientsJson` | text, default `""` | Recipients (JSON): When this is filled in it replaces To, Cc and Bcc — it is the only way to give a recipient a display name. — its `{{ }}` must give an array |
| `mergeVarsJson` | text, default `""` | Per-recipient merge values (JSON): One entry per recipient, each with the values that recipient's body uses. — its `{{ }}` must give an array |
| `recipientMetadataJson` | text, default `""` | Per-recipient metadata (JSON): Stored with that recipient's copy and searchable afterwards. — its `{{ }}` must give an array |
| `attachmentsJson` | text, default `""` | Attachments (JSON): Replaces the Attachments list above when it is filled in — use it when you need to set the type yourself. — its `{{ }}` must give an array |
| `bodyFormat` | "html" or "text" or "both", default `"html"` | Body |
| `html` | text, default `""` | HTML body — used when `bodyFormat` is one of "html", "both" |
| `text` | text, default `""` | Plain-text body — used when `bodyFormat` is one of "text", "both" |
| `sendAt` | text, default `""` | Send at: UTC, as 2026-10-01 09:00:00, at most a year ahead. A time in the past sends immediately, and scheduling needs a positive account balance. |
| `ipPool` | text, default `""` | Dedicated IP pool: The name of a dedicated IP pool on the account; empty uses the default pool. |
| `async` | true/false, default `false` | Send in the background: Every recipient comes back as queued and refusals only appear on the reject webhook. Mailchimp forces this on above 10 recipients whatever this says. |
| `failOnReject` | true/false, default `true` | Fail when a recipient is refused: Mailchimp answers HTTP 200 even when it refuses a recipient. With this on, the node fails and names the reasons; with it off, the node succeeds and you read rejected from its output. |

Output: `results` array — One entry per recipient · `messageIds` array — Message ids · `sent` number — Recipients sent to · `queued` number — Recipients queued · `scheduled` number — Recipients scheduled · `rejected` number — Recipients refused · `invalid` number — Addresses Mailchimp could not read · `rejectReasons` array — Why recipients were refused · `queuedReasons` array — Why recipients were queued · `allAccepted` boolean — Every recipient was accepted.

```json
{"type":"mandrill.sendEmail","version":1,"label":"Send email","config":{"fromName":"","to":"","cc":"","bcc":"","subject":"","attachments":{},"images":{},"globalMergeVars":{},"mergeLanguage":"mailchimp","tags":"","metadata":{},"headers":{},"subaccount":"","trackOpens":"","trackClicks":"","preserveRecipients":"","important":"","autoText":"","autoHtml":"","inlineCss":"","urlStripQs":"","viewContentLink":"","bccAddress":"","trackingDomain":"","returnPathDomain":"","googleAnalyticsDomains":"","googleAnalyticsCampaign":"","recipientsJson":"","mergeVarsJson":"","recipientMetadataJson":"","attachmentsJson":"","bodyFormat":"html","html":"","text":"","sendAt":"","ipPool":"","async":false,"failOnReject":true,"connection":"<connection id>","fromEmail":"<from address>"}}
```

## `mandrill.sendTemplate@1` — Mailchimp Transactional — Send template

Sends a transactional email from a template held in Mailchimp.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `templateName` | text, required | Template slug: The template's immutable slug, not its display name — the display name can change under you. The List templates node answers both. |
| `templateContent` | object of text, default `{}` | Editable regions: One row per mc:edit region in the template: the key is the region's name, the value the HTML that goes into it. |
| `fromEmail` | text, required | From address: Must be on a domain you verified in Mailchimp Transactional — mail from an unverified domain comes back refused with the reason unsigned. |
| `fromName` | text, default `""` | From name |
| `to` | text, default `""` | To: Comma-separated. For a list from an earlier node, write {{ join(trigger.recipients, ", ") }}. |
| `cc` | text, default `""` | Cc |
| `bcc` | text, default `""` | Bcc |
| `subject` | text, default `""` | Subject |
| `attachments` | object of text, default `{}` | Attachments: The key is the file name, the value its base64 — Pick file… fills both in. The type is taken from the file's extension; use Attachments (JSON) below to set it yourself. Everything together must stay under 25 MB, and base64 is about a third larger than the file on disk. |
| `images` | object of text, default `{}` | Inline images: The key is the content id you reference from the HTML as <img src="cid:the-key">. Only image files: Mailchimp refuses an inline attachment whose type does not start with image/. |
| `globalMergeVars` | object of text, default `{}` | Merge values: *\|NAME\|* in the body is replaced by the value here. A name may not start with an underscore. |
| `mergeLanguage` | "mailchimp" or "handlebars", default `"mailchimp"` | Merge language: In Handlebars mode the braces belong to Mailchimp, so a Flowmage expression in the body has to be written with the Fixed/Expression switch rather than inline. |
| `tags` | text, default `""` | Tags: Comma-separated. Each tag is at most 50 characters and may not start with an underscore; Mailchimp stores only the first 100 distinct tags it sees. |
| `metadata` | object of text, default `{}` | Metadata: Stored with the message and searchable afterwards. Up to 10 fields can be indexed for search in Mailchimp's own settings. |
| `headers` | object of text, default `{}` | Extra headers |
| `subaccount` | text, default `""` | Subaccount id: Routes the send through a subaccount, which must already exist. |
| `trackOpens` | "" or "on" or "off", default `""` | Track opens |
| `trackClicks` | "" or "on" or "off", default `""` | Track clicks |
| `preserveRecipients` | "" or "on" or "off", default `""` | Show every recipient in To |
| `important` | "" or "on" or "off", default `""` | Send ahead of other mail |
| `autoText` | "" or "on" or "off", default `""` | Generate a plain-text part |
| `autoHtml` | "" or "on" or "off", default `""` | Generate an HTML part |
| `inlineCss` | "" or "on" or "off", default `""` | Inline the CSS: Mailchimp only inlines CSS on HTML under 256 KB. |
| `urlStripQs` | "" or "on" or "off", default `""` | Strip query strings from tracked links |
| `viewContentLink` | "" or "on" or "off", default `""` | Log the content: Off keeps the message body out of Mailchimp's own activity log, where the Get message content node reads it from. |
| `bccAddress` | text, default `""` | Bcc every message to |
| `trackingDomain` | text, default `""` | Tracking domain: A tracking domain already set up on the account; empty uses the account's own. |
| `returnPathDomain` | text, default `""` | Return-path domain: Signing domain is deliberately not offered here: Mailchimp ignores it for every account created on or after 1 December 2015. |
| `googleAnalyticsDomains` | text, default `""` | Google Analytics domains: Comma-separated. Links to these domains get the campaign parameters added. |
| `googleAnalyticsCampaign` | text, default `""` | Google Analytics campaign |
| `recipientsJson` | text, default `""` | Recipients (JSON): When this is filled in it replaces To, Cc and Bcc — it is the only way to give a recipient a display name. — its `{{ }}` must give an array |
| `mergeVarsJson` | text, default `""` | Per-recipient merge values (JSON): One entry per recipient, each with the values that recipient's body uses. — its `{{ }}` must give an array |
| `recipientMetadataJson` | text, default `""` | Per-recipient metadata (JSON): Stored with that recipient's copy and searchable afterwards. — its `{{ }}` must give an array |
| `attachmentsJson` | text, default `""` | Attachments (JSON): Replaces the Attachments list above when it is filled in — use it when you need to set the type yourself. — its `{{ }}` must give an array |
| `html` | text, default `""` | HTML body override: Filled in, this replaces the template's HTML part for this send only. |
| `text` | text, default `""` | Plain-text body override: Filled in, this replaces the template's plain-text part for this send only. |
| `sendAt` | text, default `""` | Send at: UTC, as 2026-10-01 09:00:00, at most a year ahead. A time in the past sends immediately, and scheduling needs a positive account balance. |
| `ipPool` | text, default `""` | Dedicated IP pool: The name of a dedicated IP pool on the account; empty uses the default pool. |
| `async` | true/false, default `false` | Send in the background: Every recipient comes back as queued and refusals only appear on the reject webhook. Mailchimp forces this on above 10 recipients whatever this says. |
| `failOnReject` | true/false, default `true` | Fail when a recipient is refused: Mailchimp answers HTTP 200 even when it refuses a recipient. With this on, the node fails and names the reasons; with it off, the node succeeds and you read rejected from its output. |

Output: `results` array — One entry per recipient · `messageIds` array — Message ids · `sent` number — Recipients sent to · `queued` number — Recipients queued · `scheduled` number — Recipients scheduled · `rejected` number — Recipients refused · `invalid` number — Addresses Mailchimp could not read · `rejectReasons` array — Why recipients were refused · `queuedReasons` array — Why recipients were queued · `allAccepted` boolean — Every recipient was accepted.

```json
{"type":"mandrill.sendTemplate","version":1,"label":"Send template","config":{"templateContent":{},"fromName":"","to":"","cc":"","bcc":"","subject":"","attachments":{},"images":{},"globalMergeVars":{},"mergeLanguage":"mailchimp","tags":"","metadata":{},"headers":{},"subaccount":"","trackOpens":"","trackClicks":"","preserveRecipients":"","important":"","autoText":"","autoHtml":"","inlineCss":"","urlStripQs":"","viewContentLink":"","bccAddress":"","trackingDomain":"","returnPathDomain":"","googleAnalyticsDomains":"","googleAnalyticsCampaign":"","recipientsJson":"","mergeVarsJson":"","recipientMetadataJson":"","attachmentsJson":"","html":"","text":"","sendAt":"","ipPool":"","async":false,"failOnReject":true,"connection":"<connection id>","templateName":"<template slug>","fromEmail":"<from address>"}}
```

## `mandrill.updateTemplate@1` — Mailchimp Transactional — Update template

Changes an existing template's subject, sender or code.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mailchimp-transactional`) |
| `templateSlug` | text, required | Template slug: The template's immutable slug — the List templates node answers it beside the display name. |
| `subject` | text, default `""` | Default subject |
| `fromEmail` | text, default `""` | Default from address |
| `fromName` | text, default `""` | Default from name |
| `code` | text, default `""` | HTML: Left empty, the template's HTML is left alone; filled in, it replaces what was there. |
| `text` | text, default `""` | Plain text |
| `labels` | text, default `""` | Labels: Comma-separated, at most 10. |
| `publish` | true/false, default `true` | Publish the change: Off writes the draft only, so the send nodes keep using the version that is live now. |

Output: `template` object — The template as it now stands.

```json
{"type":"mandrill.updateTemplate","version":1,"label":"Update template","config":{"subject":"","fromEmail":"","fromName":"","code":"","text":"","labels":"","publish":true,"connection":"<connection id>","templateSlug":"<template slug>"}}
```


---

## File: references/nodes/messagebird.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# MessageBird nodes (`messagebird.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `messagebird.addContactsToGroup@1` — MessageBird — Add contacts to a group

Puts contacts into a group so one send reaches all of them.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `groupId` | text, required | Group id: The group to add to. The List groups node gives you the ids. — a value from the vendor's own list |
| `contactIds` | text, required | Contact ids: Contact ids separated by commas. MessageBird takes 50 at a time, so a longer list is sent in several calls — at most 500 in one run. — its `{{ }}` must give a string |

Output: `added` boolean — Whether every contact is in the group · `contactCount` number — Contacts added · `callCount` number — Calls it took.

```json
{"type":"messagebird.addContactsToGroup","version":1,"label":"Add contacts to a group","config":{"connection":"<connection id>","groupId":"<group id>","contactIds":"<contact ids>"}}
```

## `messagebird.checkVerificationCode@1` — MessageBird — Check verification code

Checks the one-time code a person typed.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `verifyId` | text, required | Verification id: The id Send verification code answered with. — its `{{ }}` must give a string |
| `code` | text, required | Code the person typed: Every check spends one of the attempts you allowed, whether the code is right or wrong. — its `{{ }}` must give a string |

Output: `verified` boolean — Whether the code was right · `status` string — Status (verified, failed, expired) · `verifyId` string — Verification id.

```json
{"type":"messagebird.checkVerificationCode","version":1,"label":"Check verification code","config":{"connection":"<connection id>","verifyId":"<verification id>","code":"<code the person typed>"}}
```

## `messagebird.createContact@1` — MessageBird — Create contact

Adds a person to the MessageBird contact list.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `msisdn` | text, required | Phone number: In international format. MessageBird stores it as digits, so +31 6 1234 5678 is sent as 31612345678 — spaces and the plus are removed for you. — its `{{ }}` must give a string |
| `firstName` | text, default `""` | First name — its `{{ }}` must give a string |
| `lastName` | text, default `""` | Last name — its `{{ }}` must give a string |
| `customFields` | object of text, default `{}` | Custom fields: MessageBird keeps four custom fields per contact, named custom1 to custom4. Put the name on the left and the value on the right. |

Output: `contact` object — Contact · `contactId` string — Contact id.

```json
{"type":"messagebird.createContact","version":1,"label":"Create contact","config":{"firstName":"","lastName":"","customFields":{},"connection":"<connection id>","msisdn":"<phone number>"}}
```

## `messagebird.deleteContact@1` — MessageBird — Delete contact

Removes one contact from the MessageBird contact list.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `contactId` | text, required | Contact id: The contact to remove. The List contacts node gives you the ids. — its `{{ }}` must give a string |

Output: `deleted` boolean — Whether the contact is gone · `alreadyGone` boolean — Whether it was already gone before this run.

```json
{"type":"messagebird.deleteContact","version":1,"label":"Delete contact","config":{"connection":"<connection id>","contactId":"<contact id>"}}
```

## `messagebird.getBalance@1` — MessageBird — Get balance

Reads how much credit is left in the MessageBird account.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |

Output: `amount` number — Credit left · `type` string — What the credit is counted in (euros, credits) · `payment` string — How the account pays (prepaid or postpaid).

```json
{"type":"messagebird.getBalance","version":1,"label":"Get balance","config":{"connection":"<connection id>"}}
```

## `messagebird.getContact@1` — MessageBird — Get contact

Reads one contact, with the groups and messages it belongs to.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `contactId` | text, required | Contact id: The List contacts node gives you the ids. — its `{{ }}` must give a string |

Output: `contact` object — Contact.

```json
{"type":"messagebird.getContact","version":1,"label":"Get contact","config":{"connection":"<connection id>","contactId":"<contact id>"}}
```

## `messagebird.getHlr@1` — MessageBird — Get reachability result

Reads the network's answer to the last reachability query for a number.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `phoneNumber` | text, required | Phone number: The number you asked about. Answers sent while the network is still thinking, then active, absent, unknown or failed. — its `{{ }}` must give a string |

Output: `hlr` object — The whole answer, network details included · `status` string — sent, absent, active, unknown or failed · `reachable` boolean — Whether the handset answered as reachable.

```json
{"type":"messagebird.getHlr","version":1,"label":"Get reachability result","config":{"connection":"<connection id>","phoneNumber":"<phone number>"}}
```

## `messagebird.getMessage@1` — MessageBird — Get message

Reads one sent message and the delivery status of every recipient.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `messageId` | text, required | Message id: The id Send SMS answered with. — its `{{ }}` must give a string |

Output: `message` object — Message · `status` string — Status of the send as a whole · `recipients` array — One row per number, with its status · `deliveryErrors` array — Recipients the network reported a problem for.

```json
{"type":"messagebird.getMessage","version":1,"label":"Get message","config":{"connection":"<connection id>","messageId":"<message id>"}}
```

## `messagebird.listContacts@2` — MessageBird — List contacts

Fetches the people in the MessageBird contact list.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `limit` | number, default `20` | Contacts per call: 1 to 100. MessageBird answers 20 by default. |
| `offset` | number, default `0` | Skip: How many contacts to skip. Ignored when Fetch every page is on. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the offset from the start, and refuses past 5,000 contacts rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `contacts` array — Contacts · `contactCount` number — Contacts in this answer · `hasMore` boolean — More contacts are waiting behind this page · `totalCount` number — Contacts MessageBird has in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"messagebird.listContacts","version":2,"label":"List contacts","config":{"limit":20,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `messagebird.listContacts@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `limit` | number, default `20` | Contacts per call: 1 to 100. MessageBird answers 20 by default. |
| `offset` | number, default `0` | Skip: How many contacts to skip. Ignored when Fetch every page is on. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the offset from the start, and refuses past 5,000 contacts rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":20,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"limit":20,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `messagebird.listConversationMessages@2` — MessageBird — List conversation messages

Reads the messages inside one conversation, newest first.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `conversationId` | text, required | Conversation id: The List conversations node gives you the ids. — its `{{ }}` must give a string |
| `limit` | number, default `10` | Messages per call: 1 to 20 — MessageBird's own maximum here. It answers 10 by default. |
| `offset` | number, default `0` | Skip: How many messages to skip. Ignored when Fetch every page is on. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the offset from the start. Pages here hold at most 20 rows, so this is many calls — it refuses past 1,000 messages. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `messages` array — Messages · `messageCount` number — Messages in this answer · `hasMore` boolean — More messages are waiting behind this page · `totalCount` number — Messages in the conversation in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"messagebird.listConversationMessages","version":2,"label":"List conversation messages","config":{"limit":10,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>","conversationId":"<conversation id>"}}
```

### Older version `messagebird.listConversationMessages@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `conversationId` | text, required | Conversation id: The List conversations node gives you the ids. — its `{{ }}` must give a string |
| `limit` | number, default `10` | Messages per call: 1 to 20 — MessageBird's own maximum here. It answers 10 by default. |
| `offset` | number, default `0` | Skip: How many messages to skip. Ignored when Fetch every page is on. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the offset from the start. Pages here hold at most 20 rows, so this is many calls — it refuses past 1,000 messages. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":10,"offset":0,"returnAll":false,"connection":"<connection id>","conversationId":"<conversation id>"}
{"limit":10,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>","conversationId":"<conversation id>"}
```

## `messagebird.listConversations@2` — MessageBird — List conversations

Fetches the conversations on your WhatsApp and other channels.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `limit` | number, default `10` | Conversations per call: 1 to 20 — MessageBird's own maximum here. It answers 10 by default. |
| `offset` | number, default `0` | Skip: How many conversations to skip. Ignored when Fetch every page is on. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the offset from the start. Pages here hold at most 20 rows, so this is many calls — it refuses past 1,000 conversations. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `conversations` array — Conversations · `conversationCount` number — Conversations in this answer · `hasMore` boolean — More conversations are waiting behind this page · `totalCount` number — Conversations MessageBird has in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"messagebird.listConversations","version":2,"label":"List conversations","config":{"limit":10,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `messagebird.listConversations@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `limit` | number, default `10` | Conversations per call: 1 to 20 — MessageBird's own maximum here. It answers 10 by default. |
| `offset` | number, default `0` | Skip: How many conversations to skip. Ignored when Fetch every page is on. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the offset from the start. Pages here hold at most 20 rows, so this is many calls — it refuses past 1,000 conversations. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":10,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"limit":10,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `messagebird.listGroups@2` — MessageBird — List groups

Shows the contact groups you can send a message to.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `limit` | number, default `20` | Groups per call: 1 to 200 — MessageBird's own maximum for groups. |
| `offset` | number, default `0` | Skip: How many groups to skip. Ignored when Fetch every page is on. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the offset from the start, and refuses past 5,000 groups rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `groups` array — Groups · `groupCount` number — Groups in this answer · `hasMore` boolean — More groups are waiting behind this page · `totalCount` number — Groups MessageBird has in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"messagebird.listGroups","version":2,"label":"List groups","config":{"limit":20,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `messagebird.listGroups@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `limit` | number, default `20` | Groups per call: 1 to 200 — MessageBird's own maximum for groups. |
| `offset` | number, default `0` | Skip: How many groups to skip. Ignored when Fetch every page is on. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the offset from the start, and refuses past 5,000 groups rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":20,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"limit":20,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `messagebird.listMessages@2` — MessageBird — List messages

Finds sent and received messages by sender, recipient, status or period.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `originator` | text, default `""` | Sender: Only messages sent from this sender id or number. — its `{{ }}` must give a string |
| `recipient` | text, default `""` | Phone number: Only messages to or from this number, in international format. — its `{{ }}` must give a string |
| `direction` | "any" or "mt" or "mo", default `"any"` | Direction |
| `type` | "any" or "sms" or "binary" or "flash", default `"any"` | Type |
| `status` | "any" or "scheduled" or "sent" or "buffered" or "delivered" or "expired" or "delivery_failed", default `"any"` | Status |
| `contactId` | text, default `""` | Contact id: Only messages to or from this contact. The List contacts node gives the ids. — its `{{ }}` must give a string |
| `searchterm` | text, default `""` | Text contains: Only messages whose text contains this. — its `{{ }}` must give a string |
| `from` | text, default `""` | From: The start of the period, in RFC 3339. — its `{{ }}` must give a string |
| `until` | text, default `""` | Until: The end of the period, in RFC 3339. — its `{{ }}` must give a string |
| `limit` | number, default `20` | Messages per call: 1 to 100. MessageBird answers 20 by default. |
| `offset` | number, default `0` | Skip: How many messages to skip. Ignored when Fetch every page is on. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the offset from the start, and refuses past 5,000 messages rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `messages` array — Messages · `messageCount` number — Messages in this answer · `hasMore` boolean — More messages are waiting behind this page · `totalCount` number — Messages MessageBird has in total · `nextCursor` string — Cursor for the next page.

```json
{"type":"messagebird.listMessages","version":2,"label":"List messages","config":{"originator":"","recipient":"","direction":"any","type":"any","status":"any","contactId":"","searchterm":"","from":"","until":"","limit":20,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `messagebird.listMessages@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `originator` | text, default `""` | Sender: Only messages sent from this sender id or number. — its `{{ }}` must give a string |
| `recipient` | text, default `""` | Phone number: Only messages to or from this number, in international format. — its `{{ }}` must give a string |
| `direction` | "any" or "mt" or "mo", default `"any"` | Direction |
| `type` | "any" or "sms" or "binary" or "flash", default `"any"` | Type |
| `status` | "any" or "scheduled" or "sent" or "buffered" or "delivered" or "expired" or "delivery_failed", default `"any"` | Status |
| `contactId` | text, default `""` | Contact id: Only messages to or from this contact. The List contacts node gives the ids. — its `{{ }}` must give a string |
| `searchterm` | text, default `""` | Text contains: Only messages whose text contains this. — its `{{ }}` must give a string |
| `from` | text, default `""` | From: The start of the period, in RFC 3339. — its `{{ }}` must give a string |
| `until` | text, default `""` | Until: The end of the period, in RFC 3339. — its `{{ }}` must give a string |
| `limit` | number, default `20` | Messages per call: 1 to 100. MessageBird answers 20 by default. |
| `offset` | number, default `0` | Skip: How many messages to skip. Ignored when Fetch every page is on. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks the offset from the start, and refuses past 5,000 messages rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"originator":"","recipient":"","direction":"any","type":"any","status":"any","contactId":"","searchterm":"","from":"","until":"","limit":20,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"originator":"","recipient":"","direction":"any","type":"any","status":"any","contactId":"","searchterm":"","from":"","until":"","limit":20,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `messagebird.lookupNumber@1` — MessageBird — Look up phone number

Validates a phone number and gives its country, type and formats.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `phoneNumber` | text, required | Phone number: In international format (+31612345678). A national number works too when you fill in the country below. — its `{{ }}` must give a string |
| `countryCode` | text, default `""` | Country: The two-letter country of a national number, e.g. NL. Leave it empty for an international number. |
| `failIfInvalid` | true/false, default `false` | Fail on an invalid number: Off, an unusable number answers valid = false so the flow can branch. On, the run stops there. |

Output: `lookup` object — Everything MessageBird knows about the number · `valid` boolean — Whether MessageBird could read the number · `type` string — Kind of line (mobile, fixed line, voip) · `formats` object — The number written four ways · `countryCode` string — Country the number belongs to.

```json
{"type":"messagebird.lookupNumber","version":1,"label":"Look up phone number","config":{"countryCode":"","failIfInvalid":false,"connection":"<connection id>","phoneNumber":"<phone number>"}}
```

## `messagebird.requestHlr@1` — MessageBird — Check whether a number is reachable

Asks the mobile network whether a phone is switched on right now.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `phoneNumber` | text, required | Phone number: In international format (+31612345678). The network answers a moment later — read it with Get reachability result after a Delay. — its `{{ }}` must give a string |
| `reference` | text, default `""` | Your reference: A label MessageBird echoes back with the answer. — its `{{ }}` must give a string |

Output: `hlr` object — The query MessageBird started · `hlrId` string — Query id · `status` string — Status right after asking (usually sent).

```json
{"type":"messagebird.requestHlr","version":1,"label":"Check whether a number is reachable","config":{"reference":"","connection":"<connection id>","phoneNumber":"<phone number>"}}
```

## `messagebird.sendConversationMessage@1` — MessageBird — Send WhatsApp or channel message

Sends a message on WhatsApp or another MessageBird channel.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `to` | text, required | To: The person's address on that channel — for WhatsApp, their phone number. — its `{{ }}` must give a string |
| `channelId` | text, required | Channel id: The MessageBird channel to send from, from Channels in the dashboard. It decides which product the message goes out on. — its `{{ }}` must give a string |
| `type` | "text" or "image" or "file" or "hsm", default `"text"` | Message type: Outside the 24 hours after the person last wrote to you, WhatsApp only delivers an approved template. |
| `text` | text, default `""` | Message — used when `type` is "text" |
| `mediaUrl` | text, default `""` | Address of the image or file: A public https address MessageBird can fetch the file from. — its `{{ }}` must give a string; used when `type` is one of "image", "file" |
| `hsm` | any JSON, optional | Template: The approved WhatsApp template, as JSON: its namespace, its name, the language and the values that fill its placeholders. — used when `type` is "hsm" |
| `reportUrl` | text, default `""` | Status report address: MessageBird calls this address when the status changes. |
| `trackId` | text, default `""` | Your reference: A label of at most 36 characters that MessageBird carries with the message. — its `{{ }}` must give a string |
| `fallbackChannelId` | text, default `""` | Fall back to channel: Send over this channel instead when the first one does not deliver in time. — its `{{ }}` must give a string |
| `fallbackAfter` | text, default `""` | after: How long to wait first, e.g. 5m or 1h. |

Output: `messageId` string — Message id · `status` string — accepted, pending, sent, delivered or failed.

```json
{"type":"messagebird.sendConversationMessage","version":1,"label":"Send WhatsApp or channel message","config":{"type":"text","text":"","mediaUrl":"","reportUrl":"","trackId":"","fallbackChannelId":"","fallbackAfter":"","connection":"<connection id>","to":"<to>","channelId":"<channel id>"}}
```

## `messagebird.sendSms@1` — MessageBird — Send SMS

Sends an SMS to one or more phone numbers.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `originator` | text, required | Sender: Your sender id: a phone number in international format (+31612345678), or up to 11 letters and digits. The sender must be registered for the country you are texting — several countries refuse letters, and the United States needs a registered number or short code. — its `{{ }}` must give a string |
| `audience` | "recipients" or "groups", default `"recipients"` | Send to |
| `recipients` | text, default `""` | Phone numbers: One or more numbers in international format, separated by commas. Up to 50 per call. — its `{{ }}` must give a string; used when `audience` is "recipients" |
| `groupIds` | text, default `""` | Group ids: Group ids, separated by commas. The List groups node gives you the ids. — its `{{ }}` must give a string; used when `audience` is "groups" |
| `body` | text, required | Message: 160 characters fit in one part with plain text, 70 with accents or emoji. Longer messages are split and billed per part. |
| `type` | "sms" or "flash", default `"sms"` | Type |
| `datacoding` | "auto" or "plain" or "unicode", default `"auto"` | Character set |
| `reference` | text, default `""` | Your reference: A label MessageBird echoes back and sends with the delivery report. It does not stop a duplicate send. — its `{{ }}` must give a string |
| `reportUrl` | text, default `""` | Delivery report address: MessageBird calls this address when the status changes. Leave it empty unless you have a trigger listening. |
| `scheduledDatetime` | text, default `""` | Send at: A moment in RFC 3339, e.g. 2026-10-01T09:00:00+02:00. Empty sends now. — its `{{ }}` must give a string |
| `validity` | number, optional | Valid for (seconds): MessageBird stops trying after this many seconds. Empty uses their default. |
| `shortenUrls` | true/false, default `false` | Shorten links: Replaces links in the message with short ones so they cost fewer characters. |

Output: `message` object — Message · `messageId` string — Message id — pass it to Get message · `totalCount` number — Numbers this send addressed · `totalSentCount` number — Numbers MessageBird accepted · `totalDeliveryFailedCount` number — Recipients MessageBird could not accept · `recipients` array — One row per number, with its status · `messagePartCount` number — Parts billed across every recipient.

```json
{"type":"messagebird.sendSms","version":1,"label":"Send SMS","config":{"audience":"recipients","recipients":"","groupIds":"","type":"sms","datacoding":"auto","reference":"","reportUrl":"","scheduledDatetime":"","shortenUrls":false,"connection":"<connection id>","originator":"<sender>","body":"<message>"}}
```

## `messagebird.sendVerificationCode@1` — MessageBird — Send verification code

Sends a one-time code by SMS, voice or e-mail.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `recipient` | text, required | Phone number or e-mail: Where the code goes: a number in international format, or an e-mail address when you pick E-mail below. — its `{{ }}` must give a string |
| `originator` | text, default `"Code"` | Sender: The sender the person sees. MessageBird uses Code when you leave this as it is, and the sender must be registered for the destination country. — its `{{ }}` must give a string |
| `type` | "sms" or "flash" or "tts" or "email", default `"sms"` | Send as |
| `template` | text, default `""` | Message: The text around the code. It must contain %token, which is where the code is written — leave it out and the person never sees the code. Empty uses MessageBird's own text. |
| `timeout` | number, default `30` | Valid for (seconds): 30 to 172801 seconds. MessageBird's own default is 30. |
| `tokenLength` | number, default `6` | Code length: 6 to 10 digits. |
| `maxAttempts` | number, default `1` | Attempts allowed: 1 to 10. Every check spends one, right or wrong. |
| `voice` | "default" or "male" or "female", default `"default"` | Voice — used when `type` is "tts" |
| `language` | text, default `""` | Language: The language the code is read out in. — used when `type` is "tts" |
| `subject` | text, default `""` | Subject: The subject line of the e-mail. — its `{{ }}` must give a string; used when `type` is "email" |
| `reference` | text, default `""` | Your reference: A label MessageBird echoes back. It does not stop a duplicate send. — its `{{ }}` must give a string |

Output: `verifyId` string — Verification id — Check verification code needs it · `status` string — Status (sent, expired, failed, verified) · `validUntil` string — The moment the code stops working.

```json
{"type":"messagebird.sendVerificationCode","version":1,"label":"Send verification code","config":{"originator":"Code","type":"sms","template":"","timeout":30,"tokenLength":6,"maxAttempts":1,"voice":"default","language":"","subject":"","reference":"","connection":"<connection id>","recipient":"<phone number or e-mail>"}}
```

## `messagebird.updateContact@1` — MessageBird — Update contact

Changes the name, number or custom fields of one contact.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `messagebird`) |
| `contactId` | text, required | Contact id: The contact to change. The List contacts node gives you the ids. — its `{{ }}` must give a string |
| `msisdn` | text, default `""` | Phone number: Leave a field empty to keep what MessageBird already has. — its `{{ }}` must give a string |
| `firstName` | text, default `""` | First name — its `{{ }}` must give a string |
| `lastName` | text, default `""` | Last name — its `{{ }}` must give a string |
| `customFields` | object of text, default `{}` | Custom fields: custom1 to custom4. Only the rows you fill in are changed. |

Output: `contact` object — Contact as it now stands.

```json
{"type":"messagebird.updateContact","version":1,"label":"Update contact","config":{"msisdn":"","firstName":"","lastName":"","customFields":{},"connection":"<connection id>","contactId":"<contact id>"}}
```


---

## File: references/nodes/mollie.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Mollie nodes (`mollie.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `mollie.cancelPayment@1` — Mollie — Cancel payment

Cancels a Mollie payment the customer has not paid yet.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `paymentId` | text, required | Payment id: Only a payment Mollie still calls cancelable can be cancelled; a paid or expired one cannot. |

Output: `paymentId` string — Payment id · `payment` object — Payment · `status` string — Status · `alreadyCanceled` boolean — It was already cancelled.

```json
{"type":"mollie.cancelPayment","version":1,"label":"Cancel payment","config":{"connection":"<connection id>","paymentId":"<payment id>"}}
```

## `mollie.cancelRefund@1` — Mollie — Cancel refund

Stops a queued Mollie refund before the money leaves your balance.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `paymentId` | text, required | Payment id |
| `refundId` | text, required | Refund id: Only a refund Mollie still calls queued or pending can be stopped; once it is processing the money is on its way. |

Output: `refundId` string — Refund id · `canceled` boolean — The refund was stopped.

```json
{"type":"mollie.cancelRefund","version":1,"label":"Cancel refund","config":{"connection":"<connection id>","paymentId":"<payment id>","refundId":"<refund id>"}}
```

## `mollie.cancelSubscription@1` — Mollie — Cancel subscription

Stops a running Mollie subscription so it charges nothing further.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `customerId` | text, required | Customer id |
| `subscriptionId` | text, required | Subscription id: A subscription Mollie already stopped counts as done: the run continues and says so instead of failing. |

Output: `subscriptionId` string — Subscription id · `subscription` object — Subscription · `status` string — Status · `alreadyCanceled` boolean — It was already stopped.

```json
{"type":"mollie.cancelSubscription","version":1,"label":"Cancel subscription","config":{"connection":"<connection id>","customerId":"<customer id>","subscriptionId":"<subscription id>"}}
```

## `mollie.createCapture@1` — Mollie — Capture payment

Collects money on a Mollie payment that was only authorised.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `paymentId` | text, required | Payment id: A payment created with Capture set to manual, which is now ready to collect. |
| `amountCurrency` | "EUR" or "GBP" or "CHF" or "DKK" or "NOK" or "PLN" or "SEK" or "USD" or "CZK" or "HUF" or "AUD" or "CAD" or "RON", default `"EUR"` | Currency |
| `amountValue` | text, default `""` | Amount: Leave empty to collect the whole authorised amount. — its `{{ }}` must give a string |
| `description` | text, default `""` | Description |
| `metadata` | object of text, default `{}` | Your own data |

Output: `captureId` string — Capture id · `capture` object — Capture · `status` string — Status · `replayed` boolean — Mollie replayed an earlier attempt.

```json
{"type":"mollie.createCapture","version":1,"label":"Capture payment","config":{"amountCurrency":"EUR","amountValue":"","description":"","metadata":{},"connection":"<connection id>","paymentId":"<payment id>"}}
```

## `mollie.createCustomer@1` — Mollie — Create customer

Stores a customer at Mollie so later payments can be charged to them.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `name` | text, default `""` | Name |
| `email` | text, default `""` | E-mail |
| `locale` | "" or "en_US" or "en_GB" or "nl_NL" or "nl_BE" or "fr_FR" or "fr_BE" or "de_DE" or "de_AT" or "de_CH" or "es_ES" or "ca_ES" or "pt_PT" or "it_IT" or "nb_NO" or "sv_SE" or "fi_FI" or "da_DK" or "is_IS" or "hu_HU" or "pl_PL" or "lv_LV" or "lt_LT", default `""` | Language |
| `metadata` | object of text, default `{}` | Your own data |

Output: `customerId` string — Customer id · `customer` object — Customer · `replayed` boolean — Mollie replayed an earlier attempt.

```json
{"type":"mollie.createCustomer","version":1,"label":"Create customer","config":{"name":"","email":"","locale":"","metadata":{},"connection":"<connection id>"}}
```

## `mollie.createCustomerPayment@1` — Mollie — Charge customer

Charges a stored Mollie customer on the mandate they already signed.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `customerId` | text, required | Customer id: The cst_… id. The customer needs a valid mandate — a first payment with type first, or a Create mandate node, gets them one. |
| `amountCurrency` | "EUR" or "GBP" or "CHF" or "DKK" or "NOK" or "PLN" or "SEK" or "USD" or "CZK" or "HUF" or "AUD" or "CAD" or "RON", default `"EUR"` | Currency |
| `amountValue` | text, required | Amount — its `{{ }}` must give a string |
| `description` | text, required | Description |
| `mandateId` | text, default `""` | Mandate id: Only when the customer has several — otherwise Mollie picks the valid one. |
| `webhookUrl` | text, default `""` | Webhook address |
| `metadata` | object of text, default `{}` | Your own data |

Output: `paymentId` string — Payment id · `payment` object — Payment · `status` string — Status · `checkoutUrl` string — Checkout address · `replayed` boolean — Mollie replayed an earlier attempt.

```json
{"type":"mollie.createCustomerPayment","version":1,"label":"Charge customer","config":{"amountCurrency":"EUR","mandateId":"","webhookUrl":"","metadata":{},"connection":"<connection id>","customerId":"<customer id>","amountValue":"<amount>","description":"<description>"}}
```

## `mollie.createMandate@1` — Mollie — Create mandate

Records a direct-debit or PayPal authorisation a customer has already signed.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `customerId` | text, required | Customer id |
| `method` | "directdebit" or "paypal" or "creditcard", default `"directdebit"` | How they pay: A mandate you took on paper. For one the customer signs online, create a payment with type first instead. |
| `consumerName` | text, required | Account holder |
| `consumerAccount` | text, default `""` | IBAN — used when `method` is "directdebit" |
| `consumerBic` | text, default `""` | BIC — used when `method` is "directdebit" |
| `consumerEmail` | text, default `""` | PayPal e-mail — used when `method` is "paypal" |
| `signatureDate` | text, default `""` | Signed on |
| `mandateReference` | text, default `""` | Your own reference |

Output: `mandateId` string — Mandate id · `mandate` object — Mandate · `status` string — Status · `replayed` boolean — Mollie replayed an earlier attempt.

```json
{"type":"mollie.createMandate","version":1,"label":"Create mandate","config":{"method":"directdebit","consumerAccount":"","consumerBic":"","consumerEmail":"","signatureDate":"","mandateReference":"","connection":"<connection id>","customerId":"<customer id>","consumerName":"<account holder>"}}
```

## `mollie.createPayment@1` — Mollie — Create payment

Creates a Mollie payment and hands back the checkout link.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `amountCurrency` | "EUR" or "GBP" or "CHF" or "DKK" or "NOK" or "PLN" or "SEK" or "USD" or "CZK" or "HUF" or "AUD" or "CAD" or "RON", default `"EUR"` | Currency |
| `amountValue` | text, required | Amount: The exact amount as Mollie wants it: text with two decimals, like 10.00. A number is refused. — its `{{ }}` must give a string |
| `description` | text, required | Description: What your customer sees on their bank statement. At most 255 characters. |
| `redirectUrl` | text, default `""` | Redirect address: Where Mollie sends the customer once they are done paying. |
| `cancelUrl` | text, default `""` | Cancel address |
| `webhookUrl` | text, default `""` | Webhook address: Mollie posts the payment id here on every status change. |
| `method` | text, default `""` | Payment method: Leave empty to let the customer choose. Several methods go in as a comma-separated list. |
| `locale` | "" or "en_US" or "en_GB" or "nl_NL" or "nl_BE" or "fr_FR" or "fr_BE" or "de_DE" or "de_AT" or "de_CH" or "es_ES" or "ca_ES" or "pt_PT" or "it_IT" or "nb_NO" or "sv_SE" or "fi_FI" or "da_DK" or "is_IS" or "hu_HU" or "pl_PL" or "lv_LV" or "lt_LT", default `""` | Checkout language |
| `sequenceType` | "oneoff" or "first" or "recurring", default `"oneoff"` | Payment type |
| `customerId` | text, default `""` | Customer id: The cst_… id a Create customer node made. — used when `sequenceType` is not "oneoff" |
| `mandateId` | text, default `""` | Mandate id — used when `sequenceType` is not "oneoff" |
| `captureMode` | "automatic" or "manual", default `"automatic"` | Capture |
| `captureDelay` | text, default `""` | Capture after: Mollie's own words, up to seven days: 8 hours, 2 days. — used when `captureMode` is "automatic" |
| `dueDate` | text, default `""` | Due date |
| `restrictToCountry` | text, default `""` | Only methods for country: A two-letter country code, like NL. |
| `billingAddress` | object of text, default `{}` | Billing address |
| `shippingAddress` | object of text, default `{}` | Shipping address |
| `lines` | text, default `""` | Order lines: Required for Klarna, Billie, Billink, in3, Riverty and vouchers. totalAmount must equal unitPrice x quantity minus discountAmount. |
| `metadata` | object of text, default `{}` | Your own data: Mollie stores about 1 kB and hands it back on every read and webhook. |
| `includeQrCode` | true/false, default `false` | Ask for a QR code |

Output: `paymentId` string — Payment id · `payment` object — Payment · `checkoutUrl` string — Checkout address · `status` string — Status · `replayed` boolean — Mollie replayed an earlier attempt.

```json
{"type":"mollie.createPayment","version":1,"label":"Create payment","config":{"amountCurrency":"EUR","redirectUrl":"","cancelUrl":"","webhookUrl":"","method":"","locale":"","sequenceType":"oneoff","customerId":"","mandateId":"","captureMode":"automatic","captureDelay":"","dueDate":"","restrictToCountry":"","billingAddress":{},"shippingAddress":{},"lines":"","metadata":{},"includeQrCode":false,"connection":"<connection id>","amountValue":"<amount>","description":"<description>"}}
```

## `mollie.createPaymentLink@1` — Mollie — Create payment link

Creates a shareable Mollie payment link you can send to a customer.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `description` | text, required | Description: What the customer sees on the payment page. At most 255 characters. |
| `amountCurrency` | "EUR" or "GBP" or "CHF" or "DKK" or "NOK" or "PLN" or "SEK" or "USD" or "CZK" or "HUF" or "AUD" or "CAD" or "RON", default `"EUR"` | Currency |
| `amountValue` | text, default `""` | Amount: Text with two decimals, like 25.00. Leave it empty and fill in a minimum instead to let the customer choose what to pay. — its `{{ }}` must give a string |
| `minimumAmountValue` | text, default `""` | Minimum amount: An open link the customer decides the amount on. Cannot go with a fixed amount. — its `{{ }}` must give a string |
| `redirectUrl` | text, default `""` | Redirect address |
| `webhookUrl` | text, default `""` | Webhook address |
| `reusable` | true/false, default `false` | May be paid more than once: Off, the link stops working after the first payment. |
| `expiresAt` | text, default `""` | Expires at |
| `allowedMethods` | text, default `""` | Only these methods: A comma-separated list. Leave empty to offer everything the profile has. |
| `sequenceType` | "oneoff" or "first", default `"oneoff"` | Payment type |
| `customerId` | text, default `""` | Customer id — used when `sequenceType` is "first" |
| `lines` | text, default `""` | Order lines |

Output: `paymentLinkId` string — Payment link id · `paymentLinkUrl` string — Address to send the customer · `paymentLink` object — Payment link · `replayed` boolean — Mollie replayed an earlier attempt.

```json
{"type":"mollie.createPaymentLink","version":1,"label":"Create payment link","config":{"amountCurrency":"EUR","amountValue":"","minimumAmountValue":"","redirectUrl":"","webhookUrl":"","reusable":false,"expiresAt":"","allowedMethods":"","sequenceType":"oneoff","customerId":"","lines":"","connection":"<connection id>","description":"<description>"}}
```

## `mollie.createRefund@1` — Mollie — Create refund

Refunds money on a Mollie payment, in whole or in part.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `paymentId` | text, required | Payment id: The tr_… id of the payment to give money back on. |
| `amountCurrency` | "EUR" or "GBP" or "CHF" or "DKK" or "NOK" or "PLN" or "SEK" or "USD" or "CZK" or "HUF" or "AUD" or "CAD" or "RON", default `"EUR"` | Currency |
| `amountValue` | text, required | Amount: Text with two decimals, like 10.00. Less than the payment refunds a part of it. — its `{{ }}` must give a string |
| `description` | text, default `""` | Description: Mollie may show this to your customer. At most 255 characters. |
| `externalReferenceId` | text, default `""` | Acquirer reference: Your own reference at the card acquirer, for reconciliation. |
| `metadata` | object of text, default `{}` | Your own data |

Output: `refundId` string — Refund id · `refund` object — Refund · `status` string — Status · `replayed` boolean — Mollie replayed an earlier attempt.

```json
{"type":"mollie.createRefund","version":1,"label":"Create refund","config":{"amountCurrency":"EUR","description":"","externalReferenceId":"","metadata":{},"connection":"<connection id>","paymentId":"<payment id>","amountValue":"<amount>"}}
```

## `mollie.createSalesInvoice@1` — Mollie — Create sales invoice

Issues an invoice to your customer through Mollie, with a link to pay it.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `status` | "draft" or "issued" or "paid", default `"draft"` | Issue as |
| `recipientIdentifier` | text, required | Your reference for the recipient: Your own customer number. Mollie uses it to recognise the same recipient again. |
| `recipient` | text, default `""` | Recipient: Who the invoice is for. A business recipient carries other fields than a person. |
| `lines` | text, default `""` | Invoice lines: Description at most 100 characters, quantity at least 1, and a VAT rate per line. |
| `paymentTerm` | "" or "7 days" or "14 days" or "30 days" or "45 days" or "60 days" or "90 days" or "120 days", default `""` | Payment term |
| `emailDetails` | text, default `""` | E-mail details: Mollie needs these to send the invoice. — used when `status` is "issued" |
| `paymentDetails` | text, default `""` | Payment details: Mollie needs these to book an invoice that is already paid. — used when `status` is "paid" |
| `customerId` | text, default `""` | Customer id: With a mandate id, Mollie collects the invoice by itself. |
| `mandateId` | text, default `""` | Mandate id |
| `memo` | text, default `""` | Note on the invoice |
| `discount` | text, default `""` | Discount |

Output: `salesInvoiceId` string — Invoice id · `salesInvoice` object — Invoice · `status` string — Status · `paymentUrl` string — Address for the customer to pay · `pdfUrl` string — Address of the PDF · `replayed` boolean — Mollie replayed an earlier attempt.

```json
{"type":"mollie.createSalesInvoice","version":1,"label":"Create sales invoice","config":{"status":"draft","recipient":"","lines":"","paymentTerm":"","emailDetails":"","paymentDetails":"","customerId":"","mandateId":"","memo":"","discount":"","connection":"<connection id>","recipientIdentifier":"<your reference for the recipient>"}}
```

## `mollie.createSubscription@1` — Mollie — Create subscription

Starts a repeating charge on a Mollie customer's mandate.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `customerId` | text, required | Customer id |
| `amountCurrency` | "EUR" or "GBP" or "CHF" or "DKK" or "NOK" or "PLN" or "SEK" or "USD" or "CZK" or "HUF" or "AUD" or "CAD" or "RON", default `"EUR"` | Currency |
| `amountValue` | text, required | Amount each time — its `{{ }}` must give a string |
| `interval` | text, required | How often: Mollie's own words. At most one year between charges. |
| `description` | text, required | Description: Mollie needs this to be unique among the customer's running subscriptions, and shows it on the bank statement. |
| `times` | text, default `""` | Number of charges: Leave empty to charge until it is cancelled. |
| `startDate` | text, default `""` | First charge on |
| `method` | text, default `""` | Payment method |
| `mandateId` | text, default `""` | Mandate id |
| `webhookUrl` | text, default `""` | Webhook address |
| `metadata` | object of text, default `{}` | Your own data |

Output: `subscriptionId` string — Subscription id · `subscription` object — Subscription · `status` string — Status · `nextPaymentDate` string — Next charge on · `replayed` boolean — Mollie replayed an earlier attempt.

```json
{"type":"mollie.createSubscription","version":1,"label":"Create subscription","config":{"amountCurrency":"EUR","times":"","startDate":"","method":"","mandateId":"","webhookUrl":"","metadata":{},"connection":"<connection id>","customerId":"<customer id>","amountValue":"<amount each time>","interval":"<how often>","description":"<description>"}}
```

## `mollie.deleteCustomer@1` — Mollie — Delete customer

Removes a stored customer from Mollie for good.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `customerId` | text, required | Customer id: A customer Mollie no longer knows counts as done: the run continues and says so instead of failing. |

Output: `customerId` string — Customer id · `deleted` boolean — This run removed the customer.

```json
{"type":"mollie.deleteCustomer","version":1,"label":"Delete customer","config":{"connection":"<connection id>","customerId":"<customer id>"}}
```

## `mollie.getCurrentProfile@1` — Mollie — Get current profile

Answers which website profile this Mollie connection acts for.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Answers for a website API key. An organization access token reaches several profiles and has no current one. — a connection id from `requires.connections` (provider `mollie`) |

Output: `profile` object — Profile · `profileId` string — Profile id · `name` string — Name · `website` string — Website · `mode` string — Test or live.

```json
{"type":"mollie.getCurrentProfile","version":1,"label":"Get current profile","config":{"connection":"<connection id>"}}
```

## `mollie.getCustomer@1` — Mollie — Get customer

Reads one stored Mollie customer by id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `customerId` | text, required | Customer id |

Output: `customer` object — Customer · `name` string — Name · `email` string — E-mail · `metadata` any — Your own data.

```json
{"type":"mollie.getCustomer","version":1,"label":"Get customer","config":{"connection":"<connection id>","customerId":"<customer id>"}}
```

## `mollie.getPayment@1` — Mollie — Get payment

Reads one Mollie payment, with its status and what has been refunded.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `paymentId` | text, required | Payment id: The tr_… id Mollie gave when the payment was created. |
| `embedRefunds` | true/false, default `false` | Include refunds |
| `embedChargebacks` | true/false, default `false` | Include chargebacks |
| `embedCaptures` | true/false, default `false` | Include captures |
| `includeQrCode` | true/false, default `false` | Include the QR code |

Output: `payment` object — Payment · `status` string — Status · `paidAt` string — Paid at · `amount` any — Amount · `amountRefunded` any — Amount refunded · `method` string — Method · `metadata` any — Your own data.

```json
{"type":"mollie.getPayment","version":1,"label":"Get payment","config":{"embedRefunds":false,"embedChargebacks":false,"embedCaptures":false,"includeQrCode":false,"connection":"<connection id>","paymentId":"<payment id>"}}
```

## `mollie.getPaymentLink@1` — Mollie — Get payment link

Reads one Mollie payment link, and what has been paid on it.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `paymentLinkId` | text, required | Payment link id |
| `includePayments` | true/false, default `false` | Include the payments: Makes a second read for the payments customers made on this link. |

Output: `paymentLink` object — Payment link · `paymentLinkUrl` string — Address to send the customer · `payments` array — Payments made on this link · `paymentCount` number — Payments in this answer.

```json
{"type":"mollie.getPaymentLink","version":1,"label":"Get payment link","config":{"includePayments":false,"connection":"<connection id>","paymentLinkId":"<payment link id>"}}
```

## `mollie.listBalanceTransactions@1` — Mollie — List balance transactions

Lists the movements on a Mollie balance for the bookkeeping.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Needs a connection holding an organization access token, not a website key. — a connection id from `requires.connections` (provider `mollie`) |
| `balanceId` | text, default `"primary"` | Balance: The bal_… id, or the word primary for your main balance. |
| `from` | text, default `""` | Start at movement id |
| `limit` | number, default `50` | Movements per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 movements rather than answering a short list. |

Output: `transactions` array — Movements · `transactionCount` number — Movements in this answer · `hasMore` boolean — More movements are waiting behind this page · `nextCursor` string — Start the next call at this movement id.

```json
{"type":"mollie.listBalanceTransactions","version":1,"label":"List balance transactions","config":{"balanceId":"primary","from":"","limit":50,"returnAll":false,"connection":"<connection id>"}}
```

## `mollie.listChargebacks@1` — Mollie — List chargebacks

Lists the chargebacks customers filed against your Mollie payments.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `scope` | "all" or "payment", default `"all"` | Which chargebacks |
| `paymentId` | text, default `""` | Payment id — used when `scope` is "payment" |
| `embedPayment` | true/false, default `false` | Include each payment — used when `scope` is "all" |
| `from` | text, default `""` | Start at chargeback id |
| `limit` | number, default `50` | Chargebacks per call |
| `sort` | "desc" or "asc", default `"desc"` | Order |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 chargebacks rather than answering a short list. |

Output: `chargebacks` array — Chargebacks · `chargebackCount` number — Chargebacks in this answer · `hasMore` boolean — More chargebacks are waiting behind this page · `nextCursor` string — Start the next call at this chargeback id.

```json
{"type":"mollie.listChargebacks","version":1,"label":"List chargebacks","config":{"scope":"all","paymentId":"","embedPayment":false,"from":"","limit":50,"sort":"desc","returnAll":false,"connection":"<connection id>"}}
```

## `mollie.listCustomers@1` — Mollie — List customers

Lists the customers stored at Mollie.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `from` | text, default `""` | Start at customer id |
| `limit` | number, default `50` | Customers per call |
| `sort` | "desc" or "asc", default `"desc"` | Order |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 customers rather than answering a short list. |

Output: `customers` array — Customers · `customerCount` number — Customers in this answer · `hasMore` boolean — More customers are waiting behind this page · `nextCursor` string — Start the next call at this customer id.

```json
{"type":"mollie.listCustomers","version":1,"label":"List customers","config":{"from":"","limit":50,"sort":"desc","returnAll":false,"connection":"<connection id>"}}
```

## `mollie.listInvoices@1` — Mollie — List invoices

Lists the invoices Mollie sent you for its own fees.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Needs a connection holding an organization access token, not a website key. — a connection id from `requires.connections` (provider `mollie`) |
| `reference` | text, default `""` | Invoice number |
| `year` | text, default `""` | Year |
| `from` | text, default `""` | Start at invoice id |
| `limit` | number, default `50` | Invoices per call |
| `sort` | "desc" or "asc", default `"desc"` | Order |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 invoices rather than answering a short list. |

Output: `invoices` array — Invoices · `invoiceCount` number — Invoices in this answer · `hasMore` boolean — More invoices are waiting behind this page · `nextCursor` string — Start the next call at this invoice id.

```json
{"type":"mollie.listInvoices","version":1,"label":"List invoices","config":{"reference":"","year":"","from":"","limit":50,"sort":"desc","returnAll":false,"connection":"<connection id>"}}
```

## `mollie.listMandates@1` — Mollie — List mandates

Lists the direct-debit authorisations one Mollie customer has signed.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `customerId` | text, required | Customer id |
| `from` | text, default `""` | Start at mandate id |
| `limit` | number, default `50` | Mandates per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 mandates rather than answering a short list. |

Output: `mandates` array — Mandates · `mandateCount` number — Mandates in this answer · `hasMore` boolean — More mandates are waiting behind this page · `nextCursor` string — Start the next call at this mandate id.

```json
{"type":"mollie.listMandates","version":1,"label":"List mandates","config":{"from":"","limit":50,"returnAll":false,"connection":"<connection id>","customerId":"<customer id>"}}
```

## `mollie.listMethods@1` — Mollie — List payment methods

Lists the ways this Mollie profile can let a customer pay.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `scope` | "enabled" or "all", default `"enabled"` | Which methods |
| `sequenceType` | "" or "oneoff" or "first" or "recurring", default `""` | For this payment type |
| `locale` | "" or "en_US" or "en_GB" or "nl_NL" or "nl_BE" or "fr_FR" or "fr_BE" or "de_DE" or "de_AT" or "de_CH" or "es_ES" or "ca_ES" or "pt_PT" or "it_IT" or "nb_NO" or "sv_SE" or "fi_FI" or "da_DK" or "is_IS" or "hu_HU" or "pl_PL" or "lv_LV" or "lt_LT", default `""` | Language |
| `amountCurrency` | "EUR" or "GBP" or "CHF" or "DKK" or "NOK" or "PLN" or "SEK" or "USD" or "CZK" or "HUF" or "AUD" or "CAD" or "RON", default `"EUR"` | Currency |
| `amountValue` | text, default `""` | For an amount of: Some methods only appear above or below a certain amount. Text, like 25.00. — its `{{ }}` must give a string |
| `billingCountry` | text, default `""` | Customer country: A two-letter country code, like NL. |
| `includeWallets` | text, default `""` | Include wallets |
| `includeIssuers` | true/false, default `false` | Include the banks per method |

Output: `methods` array — Payment methods · `methodCount` number — Methods in this answer.

```json
{"type":"mollie.listMethods","version":1,"label":"List payment methods","config":{"scope":"enabled","sequenceType":"","locale":"","amountCurrency":"EUR","amountValue":"","billingCountry":"","includeWallets":"","includeIssuers":false,"connection":"<connection id>"}}
```

## `mollie.listPaymentLinks@1` — Mollie — List payment links

Lists the payment links this Mollie profile has handed out.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `from` | text, default `""` | Start at link id |
| `limit` | number, default `50` | Links per call |
| `sort` | "desc" or "asc", default `"desc"` | Order |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 links rather than answering a short list. |

Output: `paymentLinks` array — Payment links · `paymentLinkCount` number — Links in this answer · `hasMore` boolean — More links are waiting behind this page · `nextCursor` string — Start the next call at this link id.

```json
{"type":"mollie.listPaymentLinks","version":1,"label":"List payment links","config":{"from":"","limit":50,"sort":"desc","returnAll":false,"connection":"<connection id>"}}
```

## `mollie.listPayments@1` — Mollie — List payments

Lists the payments on this Mollie profile, newest first.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `from` | text, default `""` | Start at payment id: Continue from a tr_… id — the Next cursor of an earlier run. |
| `limit` | number, default `50` | Payments per call: 1 to 250. |
| `sort` | "desc" or "asc", default `"desc"` | Order |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 payments rather than answering a short list. |

Output: `payments` array — Payments · `paymentCount` number — Payments in this answer · `hasMore` boolean — More payments are waiting behind this page · `nextCursor` string — Start the next call at this payment id.

```json
{"type":"mollie.listPayments","version":1,"label":"List payments","config":{"from":"","limit":50,"sort":"desc","returnAll":false,"connection":"<connection id>"}}
```

## `mollie.listRefunds@1` — Mollie — List refunds

Lists Mollie refunds, of one payment or of the whole account.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `scope` | "payment" or "all", default `"payment"` | Which refunds |
| `paymentId` | text, default `""` | Payment id — used when `scope` is "payment" |
| `embedPayment` | true/false, default `false` | Include each payment — used when `scope` is "all" |
| `from` | text, default `""` | Start at refund id |
| `limit` | number, default `50` | Refunds per call |
| `sort` | "desc" or "asc", default `"desc"` | Order |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 refunds rather than answering a short list. |

Output: `refunds` array — Refunds · `refundCount` number — Refunds in this answer · `hasMore` boolean — More refunds are waiting behind this page · `nextCursor` string — Start the next call at this refund id.

```json
{"type":"mollie.listRefunds","version":1,"label":"List refunds","config":{"scope":"payment","paymentId":"","embedPayment":false,"from":"","limit":50,"sort":"desc","returnAll":false,"connection":"<connection id>"}}
```

## `mollie.listSalesInvoices@1` — Mollie — List sales invoices

Lists the invoices you issued to customers through Mollie.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `from` | text, default `""` | Start at invoice id |
| `limit` | number, default `50` | Invoices per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 invoices rather than answering a short list. |

Output: `salesInvoices` array — Sales invoices · `salesInvoiceCount` number — Invoices in this answer · `hasMore` boolean — More invoices are waiting behind this page · `nextCursor` string — Start the next call at this invoice id.

```json
{"type":"mollie.listSalesInvoices","version":1,"label":"List sales invoices","config":{"from":"","limit":50,"returnAll":false,"connection":"<connection id>"}}
```

## `mollie.listSettlements@1` — Mollie — List settlements

Lists the payouts Mollie made to your bank, or what is inside one of them.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Needs a connection holding an organization access token, not a website key. — a connection id from `requires.connections` (provider `mollie`) |
| `detail` | "settlements" or "payments" or "refunds" or "chargebacks" or "captures", default `"settlements"` | What to read |
| `settlementId` | text, default `""` | Payout id — used when `detail` is not "settlements" |
| `balanceId` | text, default `""` | Only for balance — used when `detail` is "settlements" |
| `year` | text, default `""` | Year — used when `detail` is "settlements" |
| `month` | text, default `""` | Month — used when `detail` is "settlements" |
| `currencies` | text, default `""` | Only these currencies — used when `detail` is "settlements" |
| `from` | text, default `""` | Start at id |
| `limit` | number, default `50` | Records per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 records rather than answering a short list. |

Output: `records` array — Payouts, or what is inside the one you named · `recordCount` number — Records in this answer · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Start the next call at this id.

```json
{"type":"mollie.listSettlements","version":1,"label":"List settlements","config":{"detail":"settlements","settlementId":"","balanceId":"","year":"","month":"","currencies":"","from":"","limit":50,"returnAll":false,"connection":"<connection id>"}}
```

## `mollie.listSubscriptions@1` — Mollie — List subscriptions

Lists Mollie subscriptions, of one customer or of the whole account.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `scope` | "all" or "customer", default `"all"` | Which subscriptions |
| `customerId` | text, default `""` | Customer id — used when `scope` is "customer" |
| `from` | text, default `""` | Start at subscription id |
| `limit` | number, default `50` | Subscriptions per call |
| `sort` | "desc" or "asc", default `"desc"` | Order |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Mollie's next-page link, and refuses past 10,000 subscriptions rather than answering a short list. |

Output: `subscriptions` array — Subscriptions · `subscriptionCount` number — Subscriptions in this answer · `hasMore` boolean — More subscriptions are waiting behind this page · `nextCursor` string — Start the next call at this subscription id.

```json
{"type":"mollie.listSubscriptions","version":1,"label":"List subscriptions","config":{"scope":"all","customerId":"","from":"","limit":50,"sort":"desc","returnAll":false,"connection":"<connection id>"}}
```

## `mollie.revokeMandate@1` — Mollie — Revoke mandate

Withdraws a Mollie mandate so nothing can be collected on it again.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `customerId` | text, required | Customer id |
| `mandateId` | text, required | Mandate id: A mandate Mollie no longer knows counts as done: the run continues and says so instead of failing. |

Output: `mandateId` string — Mandate id · `revoked` boolean — This run withdrew the mandate.

```json
{"type":"mollie.revokeMandate","version":1,"label":"Revoke mandate","config":{"connection":"<connection id>","customerId":"<customer id>","mandateId":"<mandate id>"}}
```

## `mollie.updateCustomer@1` — Mollie — Update customer

Changes the name, e-mail or language of a stored Mollie customer.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `customerId` | text, required | Customer id |
| `name` | text, default `""` | Name: Leave a field empty to keep what Mollie has. |
| `email` | text, default `""` | E-mail |
| `locale` | "" or "en_US" or "en_GB" or "nl_NL" or "nl_BE" or "fr_FR" or "fr_BE" or "de_DE" or "de_AT" or "de_CH" or "es_ES" or "ca_ES" or "pt_PT" or "it_IT" or "nb_NO" or "sv_SE" or "fi_FI" or "da_DK" or "is_IS" or "hu_HU" or "pl_PL" or "lv_LV" or "lt_LT", default `""` | Language |
| `metadata` | object of text, default `{}` | Your own data |

Output: `customerId` string — Customer id · `customer` object — Customer.

```json
{"type":"mollie.updateCustomer","version":1,"label":"Update customer","config":{"name":"","email":"","locale":"","metadata":{},"connection":"<connection id>","customerId":"<customer id>"}}
```

## `mollie.updatePayment@1` — Mollie — Update payment

Changes the description, addresses or webhook of an open Mollie payment.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mollie`) |
| `paymentId` | text, required | Payment id |
| `description` | text, default `""` | Description: Leave a field empty to keep what Mollie has. |
| `redirectUrl` | text, default `""` | Redirect address |
| `cancelUrl` | text, default `""` | Cancel address |
| `webhookUrl` | text, default `""` | Webhook address: Mollie sends no webhook for the change itself, only for later status changes. |
| `method` | text, default `""` | Payment method |
| `locale` | "" or "en_US" or "en_GB" or "nl_NL" or "nl_BE" or "fr_FR" or "fr_BE" or "de_DE" or "de_AT" or "de_CH" or "es_ES" or "ca_ES" or "pt_PT" or "it_IT" or "nb_NO" or "sv_SE" or "fi_FI" or "da_DK" or "is_IS" or "hu_HU" or "pl_PL" or "lv_LV" or "lt_LT", default `""` | Checkout language |
| `dueDate` | text, default `""` | Due date |
| `restrictToCountry` | text, default `""` | Only methods for country |
| `billingEmail` | text, default `""` | Billing e-mail |
| `billingAddress` | object of text, default `{}` | Billing address |
| `shippingAddress` | object of text, default `{}` | Shipping address |
| `metadata` | object of text, default `{}` | Your own data |

Output: `paymentId` string — Payment id · `payment` object — Payment · `status` string — Status.

```json
{"type":"mollie.updatePayment","version":1,"label":"Update payment","config":{"description":"","redirectUrl":"","cancelUrl":"","webhookUrl":"","method":"","locale":"","dueDate":"","restrictToCountry":"","billingEmail":"","billingAddress":{},"shippingAddress":{},"metadata":{},"connection":"<connection id>","paymentId":"<payment id>"}}
```


---

## File: references/nodes/moneybird.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Moneybird nodes (`moneybird.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `moneybird.createContact@1` — Moneybird — Create contact

Adds a customer or supplier to a Moneybird administration.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `companyName` | text, default `""` | Company name: Leave empty for a private person and fill in the two names below. |
| `firstname` | text, default `""` | First name |
| `lastname` | text, default `""` | Last name |
| `address1` | text, default `""` | Address |
| `address2` | text, default `""` | Address line 2 |
| `zipcode` | text, default `""` | Postcode |
| `city` | text, default `""` | City |
| `country` | text, default `""` | Country: A two-letter country code, like NL. |
| `phone` | text, default `""` | Phone |
| `email` | text, default `""` | E-mail |
| `customerId` | text, default `""` | Your own customer number: What the Get contact node can look this contact up by later. |
| `taxNumber` | text, default `""` | VAT number |
| `chamberOfCommerce` | text, default `""` | Chamber of Commerce number |
| `sepaIban` | text, default `""` | IBAN |
| `sepaMandateId` | text, default `""` | SEPA mandate reference |
| `deliveryMethod` | "" or "Email" or "Post" or "Manual" or "Simplerinvoicing" or "Peppol", default `""` | How invoices go out |
| `firstDueInterval` | text, default `""` | Payment term in days |
| `customFields` | object of text, default `{}` | Custom fields: Keys are Moneybird's custom-field ids, which its own settings screen shows. |

Output: `contactId` string — Contact id · `contact` object — Contact.

```json
{"type":"moneybird.createContact","version":1,"label":"Create contact","config":{"administrationId":"","companyName":"","firstname":"","lastname":"","address1":"","address2":"","zipcode":"","city":"","country":"","phone":"","email":"","customerId":"","taxNumber":"","chamberOfCommerce":"","sepaIban":"","sepaMandateId":"","deliveryMethod":"","firstDueInterval":"","customFields":{},"connection":"<connection id>"}}
```

## `moneybird.createPurchaseInvoice@1` — Moneybird — Create purchase invoice

Books a supplier bill in Moneybird, split over ledger accounts.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `contactId` | text, required | Supplier contact id: The Moneybird contact the bill came from. |
| `reference` | text, default `""` | Bill number |
| `date` | text, default `""` | Bill date |
| `dueDate` | text, default `""` | Due date |
| `currency` | text, default `""` | Currency |
| `pricesAreInclTax` | true/false, default `false` | Prices include VAT |
| `lines` | list of {description: text, amount: text, price: text, taxRateId: text, ledgerAccountId: text}, default `[]` | Lines: The two ids come from the List VAT rates and List ledger accounts nodes. Typed literally — for lines built by an earlier node use the JSON box below. |
| `linesJson` | text, default `""` | Lines as JSON: For lines an earlier node built. Cannot be filled in beside the rows above. |

Output: `purchaseInvoiceId` string — Purchase invoice id · `purchaseInvoice` object — Purchase invoice · `state` string — State · `entryNumber` string — Entry number.

```json
{"type":"moneybird.createPurchaseInvoice","version":1,"label":"Create purchase invoice","config":{"administrationId":"","reference":"","date":"","dueDate":"","currency":"","pricesAreInclTax":false,"lines":[],"linesJson":"","connection":"<connection id>","contactId":"<supplier contact id>"}}
```

## `moneybird.createSalesInvoice@1` — Moneybird — Create sales invoice

Draws up an invoice for a Moneybird contact, line by line.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `contactId` | text, required | Contact id: The Moneybird contact to invoice. The List contacts node gives you the id. |
| `reference` | text, default `""` | Your reference |
| `invoiceDate` | text, default `""` | Invoice date |
| `dueDate` | text, default `""` | Due date |
| `workflowId` | text, default `""` | Workflow id: The List workflows node gives you the id. |
| `documentStyleId` | text, default `""` | Document style id |
| `currency` | text, default `""` | Currency |
| `language` | text, default `""` | Language |
| `pricesAreInclTax` | true/false, default `false` | Prices include VAT |
| `discount` | text, default `""` | Discount in percent |
| `paymentConditions` | text, default `""` | Payment conditions |
| `lines` | list of {description: text, amount: text, price: text, taxRateId: text, ledgerAccountId: text}, default `[]` | Lines: The two ids come from the List VAT rates and List ledger accounts nodes. Typed literally — for lines built by an earlier node use the JSON box below. |
| `linesJson` | text, default `""` | Lines as JSON: For lines an earlier node built. Cannot be filled in beside the rows above. |
| `customFields` | object of text, default `{}` | Custom fields |

Output: `salesInvoiceId` string — Invoice id · `salesInvoice` object — Sales invoice · `state` string — State · `invoiceId` string — Invoice number · `totalPriceInclTax` string — Total including VAT.

```json
{"type":"moneybird.createSalesInvoice","version":1,"label":"Create sales invoice","config":{"administrationId":"","reference":"","invoiceDate":"","dueDate":"","workflowId":"","documentStyleId":"","currency":"","language":"","pricesAreInclTax":false,"discount":"","paymentConditions":"","lines":[],"linesJson":"","customFields":{},"connection":"<connection id>","contactId":"<contact id>"}}
```

## `moneybird.createTimeEntry@1` — Moneybird — Create time entry

Writes worked hours into a Moneybird administration.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `startedAt` | text, required | Started at |
| `endedAt` | text, default `""` | Ended at |
| `userId` | text, required | User id: Whose hours these are. Moneybird's settings screen shows the user ids. |
| `description` | text, default `""` | Description |
| `contactId` | text, default `""` | Contact id |
| `projectId` | text, default `""` | Project id |
| `billable` | true/false, default `true` | Billable |
| `pausedDuration` | text, default `""` | Break in seconds |

Output: `timeEntryId` string — Time entry id · `timeEntry` object — Time entry.

```json
{"type":"moneybird.createTimeEntry","version":1,"label":"Create time entry","config":{"administrationId":"","endedAt":"","description":"","contactId":"","projectId":"","billable":true,"pausedDuration":"","connection":"<connection id>","startedAt":"<started at>","userId":"<user id>"}}
```

## `moneybird.deleteContact@1` — Moneybird — Delete contact

Removes a contact from a Moneybird administration.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `contactId` | text, required | Contact id: A contact Moneybird no longer knows counts as done: the run continues and says so instead of failing. |

Output: `contactId` string — Contact id · `deleted` boolean — This run removed the contact.

```json
{"type":"moneybird.deleteContact","version":1,"label":"Delete contact","config":{"administrationId":"","connection":"<connection id>","contactId":"<contact id>"}}
```

## `moneybird.downloadSalesInvoicePdf@1` — Moneybird — Download invoice document

Fetches a Moneybird invoice as a PDF, a UBL file or a packing slip.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `salesInvoiceId` | text, required | Invoice id |
| `variant` | "pdf" or "ubl" or "packing_slip", default `"pdf"` | Which document |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"moneybird.downloadSalesInvoicePdf","version":1,"label":"Download invoice document","config":{"administrationId":"","variant":"pdf","connection":"<connection id>","salesInvoiceId":"<invoice id>"}}
```

## `moneybird.getContact@1` — Moneybird — Get contact

Reads one Moneybird contact, by its id or by your own customer number.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `lookupBy` | "id" or "customer_id", default `"id"` | Look up by |
| `contact` | text, required | Id or customer number: Moneybird ids are long numbers; keep them as text so nothing is rounded. |

Output: `contact` object — Contact · `contactId` string — Contact id · `companyName` string — Company name · `email` string — E-mail.

```json
{"type":"moneybird.getContact","version":1,"label":"Get contact","config":{"administrationId":"","lookupBy":"id","connection":"<connection id>","contact":"<id or customer number>"}}
```

## `moneybird.getSalesInvoice@1` — Moneybird — Get sales invoice

Reads one Moneybird invoice by its id, its number or your own reference.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `lookupBy` | "id" or "invoice_id" or "reference", default `"id"` | Look up by |
| `invoice` | text, required | Id, number or reference |

Output: `salesInvoice` object — Sales invoice · `salesInvoiceId` string — Invoice id · `invoiceId` string — Invoice number · `state` string — State · `totalPriceInclTax` string — Total including VAT.

```json
{"type":"moneybird.getSalesInvoice","version":1,"label":"Get sales invoice","config":{"administrationId":"","lookupBy":"id","connection":"<connection id>","invoice":"<id, number or reference>"}}
```

## `moneybird.linkFinancialMutation@1` — Moneybird — Link bank mutation

Books a Moneybird bank line against the invoice or account it belongs to.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `mutationId` | text, required | Bank mutation id: The bank line to book. The List bank mutations node gives you the id. |
| `bookingType` | "SalesInvoice" or "Document" or "LedgerAccount" or "PaymentTransactionBatch", default `"SalesInvoice"` | Book it against |
| `bookingId` | text, default `""` | Id of what it is booked against: The invoice, document or ledger-account id. |
| `price` | text, default `""` | Amount to book: Leave empty to book the whole bank line. |
| `priceBase` | text, default `""` | Amount in the administration's currency |
| `description` | text, default `""` | Description |
| `projectId` | text, default `""` | Project id |
| `paymentBatchIdentifier` | text, default `""` | Payment batch reference |
| `markOpenSepaTransactionAsPaid` | true/false, default `false` | Mark the open SEPA transaction as paid |

Output: `mutationId` string — Bank mutation id · `mutation` object — Bank mutation · `state` string — State.

```json
{"type":"moneybird.linkFinancialMutation","version":1,"label":"Link bank mutation","config":{"administrationId":"","bookingType":"SalesInvoice","bookingId":"","price":"","priceBase":"","description":"","projectId":"","paymentBatchIdentifier":"","markOpenSepaTransactionAsPaid":false,"connection":"<connection id>","mutationId":"<bank mutation id>"}}
```

## `moneybird.listAdministrations@1` — Moneybird — List administrations

Answers which Moneybird administrations this connection's token can reach.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Run this once to find the id every other Moneybird node needs, then put it on the connection. — a connection id from `requires.connections` (provider `moneybird`) |

Output: `administrations` array — Administrations · `administrationCount` number — Administrations in this answer.

```json
{"type":"moneybird.listAdministrations","version":1,"label":"List administrations","config":{"connection":"<connection id>"}}
```

## `moneybird.listContacts@2` — Moneybird — List contacts

Lists the contacts in a Moneybird administration, filtered or searched.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `query` | text, default `""` | Search: Free text over names, e-mail and customer number. Takes a value from an earlier node. |
| `filters` | list of {key: text, value: text}, default `[]` | Filters: Typed literally, not from an earlier node — put a value from a previous step in Search above. |
| `page` | number, default `1` | Page |
| `perPage` | number, default `50` | Contacts per page: 1 to 100. Moneybird's own default differs per resource, so this is always sent. |
| `returnAll` | true/false, default `false` | Fetch every page: Reads page after page, and refuses past 20,000 contacts rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `contacts` array — Contacts · `contactCount` number — Contacts on this page · `hasMore` boolean — A next page may hold more contacts · `nextPage` number — The page number to read next · `nextCursor` string — Cursor for the next page.

```json
{"type":"moneybird.listContacts","version":2,"label":"List contacts","config":{"administrationId":"","query":"","filters":[],"page":1,"perPage":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `moneybird.listContacts@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `query` | text, default `""` | Search: Free text over names, e-mail and customer number. Takes a value from an earlier node. |
| `filters` | list of {key: text, value: text}, default `[]` | Filters: Typed literally, not from an earlier node — put a value from a previous step in Search above. |
| `page` | number, default `1` | Page |
| `perPage` | number, default `50` | Contacts per page: 1 to 100. Moneybird's own default differs per resource, so this is always sent. |
| `returnAll` | true/false, default `false` | Fetch every page: Reads page after page, and refuses past 20,000 contacts rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"administrationId":"","query":"","filters":[],"page":1,"perPage":50,"returnAll":false,"connection":"<connection id>"}
{"administrationId":"","query":"","filters":[],"page":1,"perPage":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `moneybird.listFinancialMutations@1` — Moneybird — List bank mutations

Reads the bank lines in a Moneybird administration, filtered or in bulk.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `period` | text, default `""` | Period — used when `bulk` is false |
| `state` | "" or "all" or "unprocessed" or "processed" or "auto_booked", default `""` | Booking state — used when `bulk` is false |
| `mutationType` | "" or "all" or "debit" or "credit", default `""` | Direction — used when `bulk` is false |
| `financialAccountId` | text, default `""` | Bank account id — used when `bulk` is false |
| `amountFrom` | text, default `""` | Amount from — used when `bulk` is false |
| `amountTo` | text, default `""` | Amount to — used when `bulk` is false |
| `bulk` | true/false, default `false` | Read every mutation: Off, Moneybird answers at most 100 lines however you filter. On, this asks Moneybird for every id and fetches the records in batches of 100, refusing past 20,000 — the filters below do not apply to that route. |

Output: `mutations` array — Bank mutations · `mutationCount` number — Mutations in this answer · `cappedAtHundred` boolean — Moneybird's 100-result cap was reached.

```json
{"type":"moneybird.listFinancialMutations","version":1,"label":"List bank mutations","config":{"administrationId":"","period":"","state":"","mutationType":"","financialAccountId":"","amountFrom":"","amountTo":"","bulk":false,"connection":"<connection id>"}}
```

## `moneybird.listLedgerAccounts@2` — Moneybird — List ledger accounts

Lists the chart of accounts of a Moneybird administration.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `accountType` | "" or "non_current_assets" or "current_assets" or "equity" or "provisions" or "non_current_liabilities" or "current_liabilities" or "revenue" or "direct_costs" or "expenses" or "other_income_expenses", default `""` | Only this kind: Sent as Moneybird's own filter. Leave it on every account if in doubt. |
| `page` | number, default `1` | Page |
| `perPage` | number, default `100` | Accounts per page |
| `returnAll` | true/false, default `true` | Fetch every page: On by default — a chart of accounts is meant to be complete. Refuses past 20,000 accounts. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `ledgerAccounts` array — Ledger accounts · `ledgerAccountCount` number — Accounts on this page · `hasMore` boolean — A next page may hold more accounts · `nextPage` number — The page number to read next · `nextCursor` string — Cursor for the next page.

```json
{"type":"moneybird.listLedgerAccounts","version":2,"label":"List ledger accounts","config":{"administrationId":"","accountType":"","page":1,"perPage":100,"returnAll":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `moneybird.listLedgerAccounts@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `accountType` | "" or "non_current_assets" or "current_assets" or "equity" or "provisions" or "non_current_liabilities" or "current_liabilities" or "revenue" or "direct_costs" or "expenses" or "other_income_expenses", default `""` | Only this kind: Sent as Moneybird's own filter. Leave it on every account if in doubt. |
| `page` | number, default `1` | Page |
| `perPage` | number, default `100` | Accounts per page |
| `returnAll` | true/false, default `true` | Fetch every page: On by default — a chart of accounts is meant to be complete. Refuses past 20,000 accounts. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"administrationId":"","accountType":"","page":1,"perPage":100,"returnAll":true,"connection":"<connection id>"}
{"administrationId":"","accountType":"","page":1,"perPage":100,"returnAll":true,"cursor":"","connection":"<connection id>"}
```

## `moneybird.listProducts@2` — Moneybird — List products

Lists the products and services saved in a Moneybird administration.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `query` | text, default `""` | Search: Free text over the description and the title. |
| `page` | number, default `1` | Page |
| `perPage` | number, default `50` | Products per page: 1 to 100. Moneybird's own default for products is only 10, so this is always sent. |
| `returnAll` | true/false, default `false` | Fetch every page: Reads page after page, and refuses past 20,000 products rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `products` array — Products · `productCount` number — Products on this page · `hasMore` boolean — A next page may hold more products · `nextPage` number — The page number to read next · `nextCursor` string — Cursor for the next page.

```json
{"type":"moneybird.listProducts","version":2,"label":"List products","config":{"administrationId":"","query":"","page":1,"perPage":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `moneybird.listProducts@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `query` | text, default `""` | Search: Free text over the description and the title. |
| `page` | number, default `1` | Page |
| `perPage` | number, default `50` | Products per page: 1 to 100. Moneybird's own default for products is only 10, so this is always sent. |
| `returnAll` | true/false, default `false` | Fetch every page: Reads page after page, and refuses past 20,000 products rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"administrationId":"","query":"","page":1,"perPage":50,"returnAll":false,"connection":"<connection id>"}
{"administrationId":"","query":"","page":1,"perPage":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `moneybird.listPurchaseInvoices@2` — Moneybird — List purchase invoices

Lists the supplier bills booked in a Moneybird administration.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `page` | number, default `1` | Page |
| `perPage` | number, default `50` | Bills per page |
| `returnAll` | true/false, default `false` | Fetch every page: Reads page after page, and refuses past 20,000 bills rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `purchaseInvoices` array — Purchase invoices · `purchaseInvoiceCount` number — Bills on this page · `hasMore` boolean — A next page may hold more bills · `nextPage` number — The page number to read next · `nextCursor` string — Cursor for the next page.

```json
{"type":"moneybird.listPurchaseInvoices","version":2,"label":"List purchase invoices","config":{"administrationId":"","page":1,"perPage":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `moneybird.listPurchaseInvoices@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `page` | number, default `1` | Page |
| `perPage` | number, default `50` | Bills per page |
| `returnAll` | true/false, default `false` | Fetch every page: Reads page after page, and refuses past 20,000 bills rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"administrationId":"","page":1,"perPage":50,"returnAll":false,"connection":"<connection id>"}
{"administrationId":"","page":1,"perPage":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `moneybird.listSalesInvoices@2` — Moneybird — List sales invoices

Lists the invoices you sent from a Moneybird administration.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `filters` | list of {key: text, value: text}, default `[]` | Filters: State is one of draft, scheduled, open, pending_payment, reminded, late, paid, uncollectible. Values are typed literally, not taken from an earlier node. |
| `page` | number, default `1` | Page |
| `perPage` | number, default `50` | Invoices per page |
| `returnAll` | true/false, default `false` | Fetch every page: Reads page after page, and refuses past 20,000 invoices rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `salesInvoices` array — Sales invoices · `salesInvoiceCount` number — Invoices on this page · `hasMore` boolean — A next page may hold more invoices · `nextPage` number — The page number to read next · `nextCursor` string — Cursor for the next page.

```json
{"type":"moneybird.listSalesInvoices","version":2,"label":"List sales invoices","config":{"administrationId":"","filters":[],"page":1,"perPage":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `moneybird.listSalesInvoices@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `filters` | list of {key: text, value: text}, default `[]` | Filters: State is one of draft, scheduled, open, pending_payment, reminded, late, paid, uncollectible. Values are typed literally, not taken from an earlier node. |
| `page` | number, default `1` | Page |
| `perPage` | number, default `50` | Invoices per page |
| `returnAll` | true/false, default `false` | Fetch every page: Reads page after page, and refuses past 20,000 invoices rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"administrationId":"","filters":[],"page":1,"perPage":50,"returnAll":false,"connection":"<connection id>"}
{"administrationId":"","filters":[],"page":1,"perPage":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `moneybird.listTaxRates@2` — Moneybird — List VAT rates

Lists the tax rates a Moneybird administration can put on a line.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `taxRateType` | "" or "sales_invoice" or "purchase_invoice" or "general_journal_document", default `""` | For which documents |
| `page` | number, default `1` | Page |
| `perPage` | number, default `100` | Rates per page |
| `returnAll` | true/false, default `true` | Fetch every page: On by default — a rate list is meant to be complete. Refuses past 20,000 rates. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `taxRates` array — VAT rates · `taxRateCount` number — Rates on this page · `hasMore` boolean — A next page may hold more rates · `nextPage` number — The page number to read next · `nextCursor` string — Cursor for the next page.

```json
{"type":"moneybird.listTaxRates","version":2,"label":"List VAT rates","config":{"administrationId":"","taxRateType":"","page":1,"perPage":100,"returnAll":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `moneybird.listTaxRates@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `taxRateType` | "" or "sales_invoice" or "purchase_invoice" or "general_journal_document", default `""` | For which documents |
| `page` | number, default `1` | Page |
| `perPage` | number, default `100` | Rates per page |
| `returnAll` | true/false, default `true` | Fetch every page: On by default — a rate list is meant to be complete. Refuses past 20,000 rates. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"administrationId":"","taxRateType":"","page":1,"perPage":100,"returnAll":true,"connection":"<connection id>"}
{"administrationId":"","taxRateType":"","page":1,"perPage":100,"returnAll":true,"cursor":"","connection":"<connection id>"}
```

## `moneybird.listTimeEntries@2` — Moneybird — List time entries

Lists the hours written in a Moneybird administration.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `state` | "" or "all" or "open" or "non_billable", default `""` | Which hours |
| `period` | text, default `""` | Period |
| `contactId` | text, default `""` | Contact id |
| `projectId` | text, default `""` | Project id |
| `userId` | text, default `""` | User id |
| `page` | number, default `1` | Page |
| `perPage` | number, default `50` | Entries per page |
| `returnAll` | true/false, default `false` | Fetch every page: Reads page after page, and refuses past 20,000 entries rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `timeEntries` array — Time entries · `timeEntryCount` number — Entries on this page · `hasMore` boolean — A next page may hold more entries · `nextPage` number — The page number to read next · `nextCursor` string — Cursor for the next page.

```json
{"type":"moneybird.listTimeEntries","version":2,"label":"List time entries","config":{"administrationId":"","state":"","period":"","contactId":"","projectId":"","userId":"","page":1,"perPage":50,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `moneybird.listTimeEntries@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `state` | "" or "all" or "open" or "non_billable", default `""` | Which hours |
| `period` | text, default `""` | Period |
| `contactId` | text, default `""` | Contact id |
| `projectId` | text, default `""` | Project id |
| `userId` | text, default `""` | User id |
| `page` | number, default `1` | Page |
| `perPage` | number, default `50` | Entries per page |
| `returnAll` | true/false, default `false` | Fetch every page: Reads page after page, and refuses past 20,000 entries rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"administrationId":"","state":"","period":"","contactId":"","projectId":"","userId":"","page":1,"perPage":50,"returnAll":false,"connection":"<connection id>"}
{"administrationId":"","state":"","period":"","contactId":"","projectId":"","userId":"","page":1,"perPage":50,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `moneybird.listWorkflows@1` — Moneybird — List workflows

Lists the invoice and quote workflows of a Moneybird administration.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The Create sales invoice node's workflow id comes from here. — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |

Output: `workflows` array — Workflows · `workflowCount` number — Workflows in this answer.

```json
{"type":"moneybird.listWorkflows","version":1,"label":"List workflows","config":{"administrationId":"","connection":"<connection id>"}}
```

## `moneybird.registerSalesInvoicePayment@1` — Moneybird — Register invoice payment

Books a payment against a Moneybird invoice so it counts as paid.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `salesInvoiceId` | text, required | Invoice id |
| `paymentDate` | text, required | Paid on |
| `price` | text, required | Amount received: Text, like 121.00. Less than the total leaves the invoice partly open. — its `{{ }}` must give a string |
| `priceBase` | text, default `""` | Amount in the administration's currency |
| `financialAccountId` | text, default `""` | Bank account id |
| `financialMutationId` | text, default `""` | Bank mutation id: Links this payment to the bank line it came from. |
| `ledgerAccountId` | text, default `""` | Ledger account id |
| `transactionIdentifier` | text, default `""` | Transaction reference |
| `manualPaymentAction` | text, default `""` | Manual payment action |

Output: `salesInvoiceId` string — Invoice id · `payment` object — Payment · `paymentId` string — Payment id.

```json
{"type":"moneybird.registerSalesInvoicePayment","version":1,"label":"Register invoice payment","config":{"administrationId":"","priceBase":"","financialAccountId":"","financialMutationId":"","ledgerAccountId":"","transactionIdentifier":"","manualPaymentAction":"","connection":"<connection id>","salesInvoiceId":"<invoice id>","paymentDate":"<paid on>","price":"<amount received>"}}
```

## `moneybird.sendSalesInvoice@1` — Moneybird — Send sales invoice

Sends a Moneybird invoice to the customer by e-mail or post.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `salesInvoiceId` | text, required | Invoice id |
| `deliveryMethod` | "Email" or "Post" or "Manual" or "Simplerinvoicing" or "Peppol", default `"Email"` | How to send it |
| `emailAddress` | text, default `""` | E-mail address: Leave empty to use the address on the contact. — used when `deliveryMethod` is "Email" |
| `emailSubject` | text, default `""` | Subject — used when `deliveryMethod` is "Email" |
| `emailMessage` | text, default `""` | Message — used when `deliveryMethod` is "Email" |
| `sendingScheduledAt` | text, default `""` | Send on: Leave empty to send it now. |
| `deliverTime` | text, default `""` | Time of day |
| `mergeable` | true/false, default `false` | May be merged with other invoices |
| `invoiceDate` | text, default `""` | Invoice date |

Output: `salesInvoiceId` string — Invoice id · `salesInvoice` object — Sales invoice · `state` string — State.

```json
{"type":"moneybird.sendSalesInvoice","version":1,"label":"Send sales invoice","config":{"administrationId":"","deliveryMethod":"Email","emailAddress":"","emailSubject":"","emailMessage":"","sendingScheduledAt":"","deliverTime":"","mergeable":false,"invoiceDate":"","connection":"<connection id>","salesInvoiceId":"<invoice id>"}}
```

## `moneybird.updateContact@1` — Moneybird — Update contact

Changes the details of a Moneybird contact that already exists.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `contactId` | text, required | Contact id |
| `companyName` | text, default `""` | Company name: Leave a field empty to keep what Moneybird has. |
| `firstname` | text, default `""` | First name |
| `lastname` | text, default `""` | Last name |
| `address1` | text, default `""` | Address |
| `zipcode` | text, default `""` | Postcode |
| `city` | text, default `""` | City |
| `country` | text, default `""` | Country |
| `phone` | text, default `""` | Phone |
| `email` | text, default `""` | E-mail |
| `customerId` | text, default `""` | Your own customer number |
| `sepaIban` | text, default `""` | IBAN |
| `deliveryMethod` | "" or "Email" or "Post" or "Manual" or "Simplerinvoicing" or "Peppol", default `""` | How invoices go out |
| `customFields` | object of text, default `{}` | Custom fields |

Output: `contactId` string — Contact id · `contact` object — Contact.

```json
{"type":"moneybird.updateContact","version":1,"label":"Update contact","config":{"administrationId":"","companyName":"","firstname":"","lastname":"","address1":"","zipcode":"","city":"","country":"","phone":"","email":"","customerId":"","sepaIban":"","deliveryMethod":"","customFields":{},"connection":"<connection id>","contactId":"<contact id>"}}
```

## `moneybird.updateSalesInvoice@1` — Moneybird — Update sales invoice

Changes a Moneybird invoice that is still a draft.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `salesInvoiceId` | text, required | Invoice id |
| `reference` | text, default `""` | Your reference: Leave a field empty to keep what Moneybird has. |
| `invoiceDate` | text, default `""` | Invoice date |
| `dueDate` | text, default `""` | Due date |
| `currency` | text, default `""` | Currency |
| `paymentConditions` | text, default `""` | Payment conditions |
| `lines` | list of {description: text, amount: text, price: text, taxRateId: text, ledgerAccountId: text}, default `[]` | Replace the lines with: Leave empty to keep the lines the invoice has. |
| `linesJson` | text, default `""` | Replace the lines with JSON: Cannot be filled in beside the rows above. |

Output: `salesInvoiceId` string — Invoice id · `salesInvoice` object — Sales invoice · `state` string — State.

```json
{"type":"moneybird.updateSalesInvoice","version":1,"label":"Update sales invoice","config":{"administrationId":"","reference":"","invoiceDate":"","dueDate":"","currency":"","paymentConditions":"","lines":[],"linesJson":"","connection":"<connection id>","salesInvoiceId":"<invoice id>"}}
```

## `moneybird.uploadPurchaseInvoiceAttachment@1` — Moneybird — Attach file to purchase invoice

Adds the supplier's own document to a booked Moneybird bill.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `moneybird`) |
| `administrationId` | text, default `""` | Administration id: Leave empty to use the connection's administration. Pick another this token can reach, or type its id — the List administrations node gives you the ids. — a value from the vendor's own list |
| `purchaseInvoiceId` | text, required | Purchase invoice id: The bill the document belongs to. |
| `fileName` | text, required | File name: The name Moneybird stores, extension included. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — paste {{ file("name").base64 }}. — its `{{ }}` must give a string |

Output: `purchaseInvoiceId` string — Purchase invoice id · `fileName` string — File name · `size` number — Size in bytes · `attached` boolean — The file was attached.

```json
{"type":"moneybird.uploadPurchaseInvoiceAttachment","version":1,"label":"Attach file to purchase invoice","config":{"administrationId":"","connection":"<connection id>","purchaseInvoiceId":"<purchase invoice id>","fileName":"<file name>","contentBase64":"<file contents>"}}
```


---

## File: references/nodes/mssql.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# SQL Server nodes (`mssql.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `mssql.query@1` — SQL Server — Run SQL query

Reads or changes rows in a SQL Server or Azure SQL database with SQL you write.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mssql`) |
| `operation` | "select" or "execute", default `"select"` | Operation: SQL Server cannot enforce read-only per query — use a login that can only read. |
| `query` | text, required | Query: Write values from earlier steps as @p1, @p2 … and list them under Parameters. {{ }} is not filled in here. — plain text, never `{{ }}` |
| `params` | list of any JSON, default `[]` | Parameters: One value per placeholder, in order. Wrap JSON for a json column with OPENJSON(@p1). |
| `maxRows` | number, default `1000` | Max rows: Default 1,000 — up to the workspace limit. On SQL Server, limit with TOP instead of LIMIT. |
| `ifMoreRows` | "fail" or "first", default `"fail"` | If more rows — used when `operation` is "select" |

Output: `rows` array — Rows · `rowCount` number — Rows returned · `columns` array — Columns — name and type · `truncated` boolean — More rows than Max rows — only the first came back · `affectedRows` number — Rows the statement changed.

```json
{"type":"mssql.query","version":1,"label":"Run SQL query","config":{"operation":"select","params":[],"maxRows":1000,"ifMoreRows":"fail","connection":"<connection id>","query":"<query>"}}
```


---

## File: references/nodes/mysql.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# MySQL nodes (`mysql.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `mysql.query@1` — MySQL — Run SQL query

Reads or changes rows in a MySQL or MariaDB database with SQL you write.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `mysql`) |
| `operation` | "select" or "execute", default `"select"` | Operation |
| `query` | text, required | Query: Write values from earlier steps as ?, ? … and list them under Parameters. {{ }} is not filled in here. — plain text, never `{{ }}` |
| `params` | list of any JSON, default `[]` | Parameters: One value per placeholder, in order. Wrap JSON for a json column with CAST(? AS JSON). |
| `maxRows` | number, default `1000` | Max rows: Default 1,000 — up to the workspace limit. |
| `ifMoreRows` | "fail" or "first", default `"fail"` | If more rows — used when `operation` is "select" |

Output: `rows` array — Rows · `rowCount` number — Rows returned · `columns` array — Columns — name and type · `truncated` boolean — More rows than Max rows — only the first came back · `affectedRows` number — Rows the statement changed.

```json
{"type":"mysql.query","version":1,"label":"Run SQL query","config":{"operation":"select","params":[],"maxRows":1000,"ifMoreRows":"fail","connection":"<connection id>","query":"<query>"}}
```


---

## File: references/nodes/notion.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Notion nodes (`notion.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `notion.appendBlocks@1` — Notion — Append content

Adds paragraphs, headings or other blocks to a Notion page.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `blockId` | text, required | Page or block id: A page id writes at the end of that page; a block id writes inside that block. — its `{{ }}` must give a string |
| `contentFormat` | "text" or "blocks", default `"text"` | Content |
| `content` | text, default `""` | Text: Each line becomes a paragraph. — used when `contentFormat` is "text" |
| `blocks` | text, default `""` | Content blocks: At most 100 blocks, and at most two levels of nesting in one request. — used when `contentFormat` is "blocks" |
| `position` | "end" or "start" or "after_block", default `"end"` | Where |
| `afterBlockId` | text, default `""` | After this block — its `{{ }}` must give a string; used when `position` is "after_block" |

Output: `blocks` array — Blocks that were added · `blockCount` number — How many were added.

```json
{"type":"notion.appendBlocks","version":1,"label":"Append content","config":{"contentFormat":"text","content":"","blocks":"","position":"end","afterBlockId":"","connection":"<connection id>","blockId":"<page or block id>"}}
```

## `notion.archivePage@1` — Notion — Archive or restore page

Moves a Notion page to the trash, or brings it back.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `pageId` | text, required | Page id — its `{{ }}` must give a string |
| `restore` | true/false, default `false` | Bring it back instead: Notion keeps a trashed page for 30 days, so a restore works inside that month. |

Output: `pageId` string — Page id · `inTrash` boolean — It is in the trash.

```json
{"type":"notion.archivePage","version":1,"label":"Archive or restore page","config":{"restore":false,"connection":"<connection id>","pageId":"<page id>"}}
```

## `notion.createComment@1` — Notion — Add comment

Leaves a comment on a Notion page or in an existing discussion.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The integration needs the Insert comments capability for this — set it in Notion under the integration's Capabilities. — a connection id from `requires.connections` (provider `notion`) |
| `target` | "page" or "discussion", default `"page"` | Comment on |
| `pageId` | text, default `""` | Page id — its `{{ }}` must give a string; used when `target` is "page" |
| `discussionId` | text, default `""` | Discussion id: The List comments node answers a discussion id per thread. — its `{{ }}` must give a string; used when `target` is "discussion" |
| `text` | text, required | Comment |
| `displayName` | text, default `""` | Show it as: A name of your own beside the comment. Leave it empty for the integration's. — its `{{ }}` must give a string |

Output: `commentId` string — Comment id · `discussionId` string — Discussion id · `comment` object — Comment.

```json
{"type":"notion.createComment","version":1,"label":"Add comment","config":{"target":"page","pageId":"","discussionId":"","displayName":"","connection":"<connection id>","text":"<comment>"}}
```

## `notion.createPage@1` — Notion — Create page

Adds a page to a Notion database or under another page.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `parentKind` | "dataSource" or "page", default `"dataSource"` | Create it in |
| `dataSourceId` | text, default `""` | Data source id: Not the database id. In Notion open the database → ••• → Manage data sources → Copy data source ID, or run the List data sources node on the database id from the address bar. — a value from the vendor's own list; used when `parentKind` is "dataSource" |
| `parentPageId` | text, default `""` | Parent page id — its `{{ }}` must give a string; used when `parentKind` is "page" |
| `title` | text, default `""` | Title: Written into the database's title property, whatever it is called there. — its `{{ }}` must give a string |
| `lookUpPropertyTypes` | true/false, default `true` | Read the property types first: One extra read of the data source, so plain values can be written without JSON. Turn it off only if you fill in the JSON field yourself. |
| `properties` | object of text or number or true/false, default `{}` | Properties: The property name exactly as it reads in Notion, and the plain value. Several values in one property: separate them with commas. A date range is written as 2026-10-01..2026-10-05. — a value from the vendor's own list |
| `propertiesJson` | text, default `""` | Properties (JSON): Merged over the Properties field, for anything plain values cannot express. |
| `contentFormat` | "none" or "text" or "blocks", default `"none"` | Page content |
| `content` | text, default `""` | Content: Each line becomes a paragraph on the page. — used when `contentFormat` is "text" |
| `blocks` | text, default `""` | Content blocks: At most 100 blocks, and at most two levels of nesting in one request. — used when `contentFormat` is "blocks" |
| `icon` | text, default `""` | Icon emoji: One emoji, shown beside the page's name. |

Output: `pageId` string — Page id — pass it to Update page or Append content · `url` string — Open in Notion · `page` object — Page · `cutProperties` array — Properties Notion cut at 25 references — Get page reads them whole.

```json
{"type":"notion.createPage","version":1,"label":"Create page","config":{"parentKind":"dataSource","dataSourceId":"","parentPageId":"","title":"","lookUpPropertyTypes":true,"properties":{},"propertiesJson":"","contentFormat":"none","content":"","blocks":"","icon":"","connection":"<connection id>"}}
```

## `notion.deleteBlock@1` — Notion — Delete block

Moves one block of a Notion page to the trash.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `blockId` | text, required | Block id — its `{{ }}` must give a string |

Output: `deleted` boolean — Deleted · `alreadyGone` boolean — It was already gone.

```json
{"type":"notion.deleteBlock","version":1,"label":"Delete block","config":{"connection":"<connection id>","blockId":"<block id>"}}
```

## `notion.getDataSource@1` — Notion — Get database schema

Reads which properties a Notion data source has, and of which kind.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `dataSourceId` | text, required | Data source id: Not the database id. In Notion: open the database → ••• → Manage data sources → Copy data source ID. — a value from the vendor's own list |

Output: `dataSource` object — Data source · `properties` object — Properties · `propertyNames` array — Property names.

```json
{"type":"notion.getDataSource","version":1,"label":"Get database schema","config":{"connection":"<connection id>","dataSourceId":"<data source id>"}}
```

## `notion.getPage@1` — Notion — Get page

Reads one Notion page and its property values.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `pageId` | text, required | Page id: A relation, people list or text Notion cuts short at 25 references is read in full — one more request for each such property. — its `{{ }}` must give a string |
| `filterProperties` | text, default `""` | Only these properties: Pick the page's properties, or type their ids. Leave it empty for all of them. — a value from the vendor's own list |

Output: `page` object — Page · `properties` object — Property values · `url` string — Open in Notion.

```json
{"type":"notion.getPage","version":1,"label":"Get page","config":{"filterProperties":"","connection":"<connection id>","pageId":"<page id>"}}
```

## `notion.getUser@1` — Notion — Get user

Reads one person's Notion account.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `userId` | text, required | Person id: The id a people property or the List users node answers. — a value from the vendor's own list |

Output: `user` object — Person.

```json
{"type":"notion.getUser","version":1,"label":"Get user","config":{"connection":"<connection id>","userId":"<person id>"}}
```

## `notion.listBlocks@2` — Notion — List page content

Reads the blocks that make up a Notion page.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `blockId` | text, required | Page or block id: A block that says has_children holds more blocks of its own — read those with a second call on that block's id. — its `{{ }}` must give a string |
| `limit` | number, default `100` | Blocks per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Notion's cursor, and refuses past 5,000 blocks rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `blocks` array — Blocks · `blockCount` number — Blocks in this answer · `hasMore` boolean — More blocks are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"notion.listBlocks","version":2,"label":"List page content","config":{"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","blockId":"<page or block id>"}}
```

### Older version `notion.listBlocks@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `blockId` | text, required | Page or block id: A block that says has_children holds more blocks of its own — read those with a second call on that block's id. — its `{{ }}` must give a string |
| `limit` | number, default `100` | Blocks per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Notion's cursor, and refuses past 5,000 blocks rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":100,"returnAll":false,"connection":"<connection id>","blockId":"<page or block id>"}
{"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","blockId":"<page or block id>"}
```

## `notion.listComments@2` — Notion — List comments

Reads the comments on a Notion page or block.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The integration needs the Read comments capability for this — set it in Notion under the integration's Capabilities. — a connection id from `requires.connections` (provider `notion`) |
| `blockId` | text, required | Page or block id — its `{{ }}` must give a string |
| `limit` | number, default `100` | Comments per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Notion's cursor, and refuses past 5,000 comments rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `comments` array — Comments · `commentCount` number — Comments in this answer · `hasMore` boolean — More comments are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"notion.listComments","version":2,"label":"List comments","config":{"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","blockId":"<page or block id>"}}
```

### Older version `notion.listComments@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The integration needs the Read comments capability for this — set it in Notion under the integration's Capabilities. — a connection id from `requires.connections` (provider `notion`) |
| `blockId` | text, required | Page or block id — its `{{ }}` must give a string |
| `limit` | number, default `100` | Comments per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Notion's cursor, and refuses past 5,000 comments rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":100,"returnAll":false,"connection":"<connection id>","blockId":"<page or block id>"}
{"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","blockId":"<page or block id>"}
```

## `notion.listDataSources@1` — Notion — List data sources

Turns a Notion database id into the data source ids inside it.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `databaseId` | text, required | Database id: A Notion database holds one or more data sources. The id in a database's address is the database id; every other Notion node wants the data source id, which this node gives you. — its `{{ }}` must give a string |

Output: `dataSources` array — Data sources · `dataSourceCount` number — How many there are · `databaseTitle` string — Database title.

```json
{"type":"notion.listDataSources","version":1,"label":"List data sources","config":{"connection":"<connection id>","databaseId":"<database id>"}}
```

## `notion.listUsers@2` — Notion — List users

Lists the people in a Notion workspace.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The integration needs the Read user information capability for this — set it in Notion under the integration's Capabilities. — a connection id from `requires.connections` (provider `notion`) |
| `limit` | number, default `100` | People per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Notion's cursor, and refuses past 5,000 people rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `users` array — People · `userCount` number — People in this answer · `hasMore` boolean — More people are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"notion.listUsers","version":2,"label":"List users","config":{"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `notion.listUsers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The integration needs the Read user information capability for this — set it in Notion under the integration's Capabilities. — a connection id from `requires.connections` (provider `notion`) |
| `limit` | number, default `100` | People per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Notion's cursor, and refuses past 5,000 people rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":100,"returnAll":false,"connection":"<connection id>"}
{"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `notion.queryDataSource@2` — Notion — Query database

Finds pages in a Notion data source by filter and sort.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `dataSourceId` | text, required | Data source id: Not the database id. The List data sources node turns a database id into the data source ids inside it. — a value from the vendor's own list |
| `filterJson` | text, default `""` | Filter: Notion's own filter shape, which is per property type and may nest with and / or. Leave it empty for every page. |
| `sorts` | list of {property?: text, direction?: "ascending" or "descending"}, default `[]` | Sort by |
| `filterProperties` | text, default `""` | Only these properties: Pick the data source's properties, or type their ids. Leave it empty for all of them. — a value from the vendor's own list |
| `includeArchived` | true/false, default `false` | Include trashed pages |
| `limit` | number, default `100` | Pages per call: 1 to 100 — Notion's own maximum. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Notion's cursor, and refuses past 5,000 pages rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `pages` array — Pages · `pageCount` number — Pages in this answer · `hasMore` boolean — More pages are waiting behind this one · `nextCursor` string — Cursor for the next page.

```json
{"type":"notion.queryDataSource","version":2,"label":"Query database","config":{"filterJson":"","sorts":[],"filterProperties":"","includeArchived":false,"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","dataSourceId":"<data source id>"}}
```

### Older version `notion.queryDataSource@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `dataSourceId` | text, required | Data source id: Not the database id. The List data sources node turns a database id into the data source ids inside it. — a value from the vendor's own list |
| `filterJson` | text, default `""` | Filter: Notion's own filter shape, which is per property type and may nest with and / or. Leave it empty for every page. |
| `sorts` | list of {property?: text, direction?: "ascending" or "descending"}, default `[]` | Sort by |
| `filterProperties` | text, default `""` | Only these properties: Pick the data source's properties, or type their ids. Leave it empty for all of them. — a value from the vendor's own list |
| `includeArchived` | true/false, default `false` | Include trashed pages |
| `limit` | number, default `100` | Pages per call: 1 to 100 — Notion's own maximum. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Notion's cursor, and refuses past 5,000 pages rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"filterJson":"","sorts":[],"filterProperties":"","includeArchived":false,"limit":100,"returnAll":false,"connection":"<connection id>","dataSourceId":"<data source id>"}
{"filterJson":"","sorts":[],"filterProperties":"","includeArchived":false,"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","dataSourceId":"<data source id>"}
```

## `notion.search@2` — Notion — Search

Finds Notion pages and databases by title.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `query` | text, default `""` | Title contains: Search only sees pages and databases somebody has shared with this integration. Leave it empty to list everything it can see. — its `{{ }}` must give a string |
| `objectType` | "any" or "page" or "data_source", default `"any"` | Look for |
| `sortDirection` | "descending" or "ascending", default `"descending"` | Newest first |
| `inTrash` | true/false, default `false` | Search the trash instead |
| `limit` | number, default `100` | Results per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Notion's cursor, and refuses past 5,000 results rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `results` array — Pages and databases · `resultCount` number — Results in this answer · `hasMore` boolean — More results are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"notion.search","version":2,"label":"Search","config":{"query":"","objectType":"any","sortDirection":"descending","inTrash":false,"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `notion.search@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `query` | text, default `""` | Title contains: Search only sees pages and databases somebody has shared with this integration. Leave it empty to list everything it can see. — its `{{ }}` must give a string |
| `objectType` | "any" or "page" or "data_source", default `"any"` | Look for |
| `sortDirection` | "descending" or "ascending", default `"descending"` | Newest first |
| `inTrash` | true/false, default `false` | Search the trash instead |
| `limit` | number, default `100` | Results per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Notion's cursor, and refuses past 5,000 results rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"query":"","objectType":"any","sortDirection":"descending","inTrash":false,"limit":100,"returnAll":false,"connection":"<connection id>"}
{"query":"","objectType":"any","sortDirection":"descending","inTrash":false,"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `notion.updateBlock@1` — Notion — Update block

Rewrites one block on a Notion page.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `blockId` | text, required | Block id — its `{{ }}` must give a string |
| `bodyFormat` | "text" or "json", default `"text"` | New content |
| `blockType` | "paragraph" or "heading_1" or "heading_2" or "heading_3" or "to_do" or "quote", default `"paragraph"` | Kind of block: It has to be the kind the block already is — Notion cannot change that. — used when `bodyFormat` is "text" |
| `text` | text, default `""` | Text — used when `bodyFormat` is "text" |
| `checked` | true/false, default `false` | Ticked — used when `blockType` is "to_do" |
| `bodyJson` | text, default `""` | Block body (JSON): An object keyed by the block's own type. — used when `bodyFormat` is "json" |

Output: `blockId` string — Block id · `block` object — Block.

```json
{"type":"notion.updateBlock","version":1,"label":"Update block","config":{"bodyFormat":"text","blockType":"paragraph","text":"","checked":false,"bodyJson":"","connection":"<connection id>","blockId":"<block id>"}}
```

## `notion.updatePage@1` — Notion — Update page

Changes the property values of a Notion page.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `pageId` | text, required | Page id — its `{{ }}` must give a string |
| `title` | text, default `""` | Title: Leave it empty to keep the page's current title. — its `{{ }}` must give a string |
| `lookUpPropertyTypes` | true/false, default `true` | Read the property types first: One extra read of the page and its data source, so plain values can be written without JSON. |
| `properties` | object of text or number or true/false, default `{}` | Properties: The property name exactly as it reads in Notion, and the plain value. Only the properties named here change. — a value from the vendor's own list |
| `propertiesJson` | text, default `""` | Properties (JSON): Merged over the Properties field. |
| `icon` | text, default `""` | Icon emoji: Leave it empty to keep the page's current icon. |
| `isLocked` | "unchanged" or "lock" or "unlock", default `"unchanged"` | Editing by people |

Output: `pageId` string — Page id · `page` object — Page · `url` string — Open in Notion.

```json
{"type":"notion.updatePage","version":1,"label":"Update page","config":{"title":"","lookUpPropertyTypes":true,"properties":{},"propertiesJson":"","icon":"","isLocked":"unchanged","connection":"<connection id>","pageId":"<page id>"}}
```

## `notion.uploadFile@1` — Notion — Upload file

Uploads a file to Notion and attaches it to a page or a property.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `notion`) |
| `name` | text, required | File name: The file's name, extension included. Pick the file, then paste its base64 below. — a file name, listed in `requires.files` |
| `contentType` | text, default `"application/octet-stream"` | Content type |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("name").base64 }}. At most 20 MB here; a free Notion workspace caps a file at 5 MiB. Uploads over the "Use fixed IP address" lane are unverified; leave that toggle off for this node. — its `{{ }}` must give a string |
| `pageId` | text, required | Page id: The page the file is attached to. — its `{{ }}` must give a string |
| `attachAs` | "property" or "block", default `"property"` | Attach it as |
| `propertyName` | text, default `""` | Property name: The files property's name, exactly as it reads in Notion. — a value from the vendor's own list; used when `attachAs` is "property" |
| `blockType` | "file" or "image", default `"file"` | Kind of block — used when `attachAs` is "block" |

Output: `fileUploadId` string — Upload id · `fileUpload` object — Upload · `attached` boolean — Attached to the page.

```json
{"type":"notion.uploadFile","version":1,"label":"Upload file","config":{"contentType":"application/octet-stream","attachAs":"property","propertyName":"","blockType":"file","connection":"<connection id>","name":"<file name>","contentBase64":"<file contents>","pageId":"<page id>"}}
```


---

## File: references/nodes/oneflow.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Oneflow nodes (`oneflow.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `oneflow.addParticipant@1` — Oneflow — Add participant

Adds a person to an existing party on a Oneflow contract.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |
| `partyId` | text, required | Party id: The party this person joins — a number, from Get contract → parties[].id. |
| `name` | text, required | Name |
| `deliveryChannel` | "email" or "sms" or "email_and_sms" or "same_device" or "none" or "unknown", default `"email"` | Delivery |
| `email` | text, default `""` | E-mail |
| `phoneNumber` | text, default `""` | Phone number |
| `signatory` | true/false, optional | Signatory |
| `organizer` | true/false, optional | Organizer |

Output: `participant` object — Participant · `participant.id` number — Id.

```json
{"type":"oneflow.addParticipant","version":1,"label":"Add participant","config":{"deliveryChannel":"email","email":"","phoneNumber":"","connection":"<connection id>","contractId":"<contract id>","partyId":"<party id>","name":"<name>"}}
```

## `oneflow.addParty@1` — Oneflow — Add party

Adds a counterparty — a company or a person — to a Oneflow contract.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |
| `type` | "company" or "individual", default `"company"` | Party type |
| `name` | text, default `""` | Company name — used when `type` is "company" |
| `participants` | list of {name?: text, email?: text, deliveryChannel?: "email" or "sms" or "email_and_sms" or "same_device" or "none" or "unknown"}, default `[]` | Participants: The people at this company who will receive the contract. |
| `identificationNumber` | text, default `""` | Company registration number |

Output: `party` object — Party · `party.id` number — Id.

```json
{"type":"oneflow.addParty","version":1,"label":"Add party","config":{"type":"company","name":"","participants":[],"identificationNumber":"","connection":"<connection id>","contractId":"<contract id>"}}
```

## `oneflow.createContract@1` — Oneflow — Create contract

Creates a draft Oneflow contract from a template, with its parties and data fields.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `workspaceId` | text, required | Workspace id: The Oneflow workspace the contract belongs to — a number. |
| `templateId` | text, required | Template id: The Oneflow template to create from — a number. List templates hands you the ids. |
| `name` | text, default `""` | Name |
| `folderId` | text, default `""` | Folder id |
| `parties` | any JSON, optional | Parties: Counterparties as JSON, at most 40. Each is a company or an individual. |
| `dataFields` | object of text, default `{}` | Data fields: Template data fields: the field's custom id on the left, its value on the right. |
| `tags` | any JSON, optional | Tags: Tag ids as a JSON array, e.g. [12, 15]. |
| `value` | number, optional | Value |

Output: `contractId` number — Id · `contract` object — Contract · `state` string — State.

```json
{"type":"oneflow.createContract","version":1,"label":"Create contract","config":{"name":"","folderId":"","dataFields":{},"connection":"<connection id>","workspaceId":"<workspace id>","templateId":"<template id>"}}
```

## `oneflow.deleteContract@1` — Oneflow — Delete contract

Deletes a Oneflow contract.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |

Output: `deleted` boolean — Deleted.

```json
{"type":"oneflow.deleteContract","version":1,"label":"Delete contract","config":{"connection":"<connection id>","contractId":"<contract id>"}}
```

## `oneflow.downloadFile@1` — Oneflow — Download file

Downloads one file from a Oneflow contract as base64.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |
| `fileId` | text, required | File id: The file's Oneflow id — a number, from List files → files[].id. |
| `downloadAs` | text, default `""` | Download as: A name to give the download, e.g. agreement.pdf. Leave empty for Oneflow's own name. |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"oneflow.downloadFile","version":1,"label":"Download file","config":{"downloadAs":"","connection":"<connection id>","contractId":"<contract id>","fileId":"<file id>"}}
```

## `oneflow.getContract@1` — Oneflow — Get contract

Fetches one Oneflow contract with its parties, data fields and state.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. List contracts hands you contracts[].id. |

Output: `contract` object — Contract · `state` string — State · `lifecycleState` string — Lifecycle state · `parties` array — Parties · `dataFields` array — Data fields.

```json
{"type":"oneflow.getContract","version":1,"label":"Get contract","config":{"connection":"<connection id>","contractId":"<contract id>"}}
```

## `oneflow.listContracts@2` — Oneflow — List contracts

Lists Oneflow contracts, filtered by workspace, state, template or free text.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `workspaceIds` | text, default `""` | Workspaces: Comma-separated Oneflow workspace ids. Leave empty for every workspace you can see. |
| `states` | text, default `""` | States: Comma-separated: draft, pending, signed, declined. |
| `templateIds` | text, default `""` |  |
| `search` | text, default `""` | Search: Free text over the contract's name and content, at most 300 characters. |
| `sort` | text, default `"published_time"` | Sort by |
| `limit` | number, default `100` | Contracts per call: 1 to 100 — Oneflow's maximum. With Fetch every page on, this is the page size. |
| `offset` | number, default `0` | Offset: How many contracts to pass over first. Oneflow stops at 19,900 — past that, narrow the search with a filter instead. |
| `returnAll` | true/false, default `false` | Fetch every page: Pages with offset until Oneflow runs out, and refuses past 5,000 contracts rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `contracts` array — Contracts · `contractCount` number — Contracts in this answer · `totalCount` number — How many Oneflow holds in total · `hasMore` boolean — More contracts are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"oneflow.listContracts","version":2,"label":"List contracts","config":{"workspaceIds":"","states":"","templateIds":"","search":"","sort":"published_time","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `oneflow.listContracts@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `workspaceIds` | text, default `""` | Workspaces: Comma-separated Oneflow workspace ids. Leave empty for every workspace you can see. |
| `states` | text, default `""` | States: Comma-separated: draft, pending, signed, declined. |
| `templateIds` | text, default `""` |  |
| `search` | text, default `""` | Search: Free text over the contract's name and content, at most 300 characters. |
| `sort` | text, default `"published_time"` | Sort by |
| `limit` | number, default `100` | Contracts per call: 1 to 100 — Oneflow's maximum. With Fetch every page on, this is the page size. |
| `offset` | number, default `0` | Offset: How many contracts to pass over first. Oneflow stops at 19,900 — past that, narrow the search with a filter instead. |
| `returnAll` | true/false, default `false` | Fetch every page: Pages with offset until Oneflow runs out, and refuses past 5,000 contracts rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"workspaceIds":"","states":"","templateIds":"","search":"","sort":"published_time","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>"}
{"workspaceIds":"","states":"","templateIds":"","search":"","sort":"published_time","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `oneflow.listDataFields@1` — Oneflow — List data fields

Reads the data fields of one Oneflow contract and their values.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |

Output: `dataFields` array — Data fields · `values` object — Values by custom id.

```json
{"type":"oneflow.listDataFields","version":1,"label":"List data fields","config":{"connection":"<connection id>","contractId":"<contract id>"}}
```

## `oneflow.listFiles@1` — Oneflow — List files

Lists the files on a Oneflow contract — the contract PDF, attachments and verifications.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |
| `limit` | number, default `100` | Files per call: 1 to 100. |
| `offset` | number, default `0` | Offset |
| `sort` | "id" or "type" or "extension" or "name", default `"id"` | Sort by |

Output: `files` array — Files · `fileCount` number — Files in this answer · `totalCount` number — How many the contract holds in total.

```json
{"type":"oneflow.listFiles","version":1,"label":"List files","config":{"limit":100,"offset":0,"sort":"id","connection":"<connection id>","contractId":"<contract id>"}}
```

## `oneflow.listTemplates@1` — Oneflow — List templates

Lists the Oneflow templates you can create contracts from.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `workspaceIds` | text, default `""` | Workspaces: Comma-separated Oneflow workspace ids. Leave empty for every workspace you can see. |
| `active` | "" or "true" or "false", default `""` | Only active |
| `limit` | number, default `100` | Templates per call: 1 to 100. |
| `offset` | number, default `0` | Offset |
| `sort` | "name", default `"name"` |  |

Output: `templates` array — Templates · `templateCount` number — Templates in this answer · `totalCount` number — How many Oneflow holds in total.

```json
{"type":"oneflow.listTemplates","version":1,"label":"List templates","config":{"workspaceIds":"","active":"","limit":100,"offset":0,"sort":"name","connection":"<connection id>"}}
```

## `oneflow.markContractSigned@1` — Oneflow — Mark contract as signed

Records a Oneflow contract as signed outside Oneflow, on a date you give.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |
| `signedAt` | text, required | Signed at: The date and time it was signed, ISO 8601 with an offset. |

Output: `contract` object — Contract · `state` string — State.

```json
{"type":"oneflow.markContractSigned","version":1,"label":"Mark contract as signed","config":{"connection":"<connection id>","contractId":"<contract id>","signedAt":"<signed at>"}}
```

## `oneflow.publishContract@1` — Oneflow — Publish contract

Sends a Oneflow contract out for signing with a subject and a message.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |
| `subject` | text, required | Subject: 1 to 150 characters — Oneflow's own bound. |
| `message` | text, required | Message: 1 to 5,000 characters — Oneflow's own bound. |

Output: `contract` object — Contract · `state` string — State.

```json
{"type":"oneflow.publishContract","version":1,"label":"Publish contract","config":{"connection":"<connection id>","contractId":"<contract id>","subject":"<subject>","message":"<message>"}}
```

## `oneflow.setDataFields@1` — Oneflow — Set data fields

Writes values into a Oneflow contract's data fields.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |
| `fields` | object of text, default `{}` | Data fields: The field's custom id on the left, its value on the right — at most 1,024 characters per value. |

Output: `dataFields` array — Data fields · `fieldCount` number — How many values were set.

```json
{"type":"oneflow.setDataFields","version":1,"label":"Set data fields","config":{"fields":{},"connection":"<connection id>","contractId":"<contract id>"}}
```

## `oneflow.updateContract@1` — Oneflow — Update contract

Changes a Oneflow contract's name, value, signing period or tags.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |
| `name` | text, default `""` | Name: Many fields are only editable before the contract is signed. |
| `signingPeriodExpiration` | text, default `""` | Signing period ends |
| `singleSign` | true/false, optional | Single sign |
| `value` | number, optional | Value |
| `signLater` | true/false, optional | Sign later |
| `tags` | any JSON, optional | Tags: Tag ids as a JSON array, e.g. [12, 15]. |

Output: `contract` object — Contract · `state` string — State.

```json
{"type":"oneflow.updateContract","version":1,"label":"Update contract","config":{"name":"","signingPeriodExpiration":"","connection":"<connection id>","contractId":"<contract id>"}}
```

## `oneflow.uploadFile@1` — Oneflow — Upload file

Attaches a file to a Oneflow contract.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `oneflow`) |
| `contractId` | text, required | Contract id: The contract's Oneflow id — a number. |
| `name` | text, required | File name: The file's name, extension included. Pick the file, then paste its base64 below. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("name").base64 }}. Multipart uploads over the "Use fixed IP address" lane are unverified; leave that toggle off for this node. — its `{{ }}` must give a string |
| `uploadAs` | "attachment" or "expanded_pdf", default `"attachment"` | Upload as |

Output: `file` object — File · `file.id` number — Id.

```json
{"type":"oneflow.uploadFile","version":1,"label":"Upload file","config":{"uploadAs":"attachment","connection":"<connection id>","contractId":"<contract id>","name":"<file name>","contentBase64":"<file contents>"}}
```


---

## File: references/nodes/openai.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# OpenAI nodes (`openai.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `openai.classify@1` — OpenAI — Classify

Sorts a text into one of your labels with an OpenAI model and sends the flow down that path.

Ports: one per row `id` in `labels`, plus `other` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Your own OpenAI API key. Every call runs on it and is billed by OpenAI to your account. — a connection id from `requires.connections` (provider `openai`) |
| `model` | text, required | Model: The models your OpenAI connection listed when its key was saved. Refresh the connection to see a model OpenAI added since. Automatic retries only help when the model never started answering. — an AI model id |
| `text` | text, required | Text: What is being sorted. Everything here is sent to the model. |
| `labels` | list of {id: text, label: text, description?: text}, default `[]` | Labels: Each label becomes an output on the canvas. Because this step decides where the flow goes next, it cannot be turned off and it cannot continue on error — delete it instead. |
| `whenNoMatch` | "other" or "fail", default `"other"` | When nothing fits: Every text ends somewhere: down the Other path, or with the step failing. A label the model invents counts as nothing fitting. |
| `instructions` | text, default `""` | Instructions: Anything the model should know: what a borderline text should count as, or which label wins when two could fit. |
| `reasoning` | "default" or "low" or "medium" or "high", default `"default"` | Reasoning effort: How much an OpenAI reasoning model — GPT-5 and later, and the o-series — reasons before it answers. Model default leaves it to the model; GPT-5.5 and GPT-5.6 reason at medium unless told otherwise. GPT-4.1 and GPT-4o do not reason and refuse any level, so leave them on Model default. Reasoning is billed by OpenAI to your account as output tokens, gets its own room on top of the answer's output ceiling (Maximum length, where the step has one), and makes the step slower. |

Output: shaped by the config.

```json
{"type":"openai.classify","version":1,"label":"Classify","config":{"labels":[],"whenNoMatch":"other","instructions":"","reasoning":"default","connection":"<connection id>","model":"<model>","text":"<text>"}}
```

## `openai.extract@1` — OpenAI — Extract fields

Pulls the values you name out of a text or a document with an OpenAI model, each typed.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Your own OpenAI API key. Every call runs on it and is billed by OpenAI to your account. — a connection id from `requires.connections` (provider `openai`) |
| `model` | text, required | Model: The models your OpenAI connection listed when its key was saved. Refresh the connection to see a model OpenAI added since. Automatic retries only help when the model never started answering. — an AI model id |
| `source` | "text" or "file", default `"text"` | Read from: Text you point at with an expression, or a file in this workspace. |
| `text` | text, default `""` | Text: Everything here is sent to the model — point at the part that holds the values. — used when `source` is "text" |
| `file` | text, default `""` | File: OpenAI reads a PDF, or a PNG, JPEG, WebP or non-animated GIF image, itself on a model that takes images (GPT-4o and later). Word, Excel, OpenDocument, HTML, XML and CSV files are read as text first, and plain text as it is. Up to 5 MB. — a file name, listed in `requires.files`; used when `source` is "file" |
| `fields` | list of {name: text, description?: text, type?: "text" or "number" or "boolean" or "date" or "text-list" or "number-list", required?: true/false}, optional | Fields: Add the fields you want back. A sentence saying where to find each one makes this much more accurate — “the total including VAT, near the bottom”. |
| `instructions` | text, default `""` | Instructions: Anything the model should know about the source before it reads it. |
| `whenMissing` | "empty" or "fail", default `"empty"` | When a field is missing: A value the source does not carry comes back empty. Failing instead stops the run on any field you marked required, and says which one. |
| `useJsonSchema` | true/false, default `false` | Use JSON Schema instead of fields: For an answer the field rows cannot describe — nesting, enums, a list of objects. It replaces the fields above entirely. |
| `schema` | object (any keys), default `{}` | Schema: A JSON Schema document. The answer is validated against it and one repair round is allowed before the step fails. — used when `useJsonSchema` is true |
| `reasoning` | "default" or "low" or "medium" or "high", default `"default"` | Reasoning effort: How much an OpenAI reasoning model — GPT-5 and later, and the o-series — reasons before it answers. Model default leaves it to the model; GPT-5.5 and GPT-5.6 reason at medium unless told otherwise. GPT-4.1 and GPT-4o do not reason and refuse any level, so leave them on Model default. Reasoning is billed by OpenAI to your account as output tokens, gets its own room on top of the answer's output ceiling (Maximum length, where the step has one), and makes the step slower. |

Output: shaped by the config.

```json
{"type":"openai.extract","version":1,"label":"Extract fields","config":{"source":"text","text":"","file":"","instructions":"","whenMissing":"empty","useJsonSchema":false,"schema":{},"reasoning":"default","connection":"<connection id>","model":"<model>","fields":[]}}
```

## `openai.generate@1` — OpenAI — Generate text

Asks an OpenAI model to write something from your instructions and the flow's data.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: Your own OpenAI API key. Every call runs on it and is billed by OpenAI to your account. — a connection id from `requires.connections` (provider `openai`) |
| `model` | text, required | Model: The models your OpenAI connection listed when its key was saved. Refresh the connection to see a model OpenAI added since. Automatic retries only help when the model never started answering. — an AI model id |
| `task` | text, default `"Custom"` | What to do: Picks the standing instruction the model gets before your prompt. Complete a suggestion in your own words — “Translate to Dutch” — or choose Custom and say everything in the prompt. |
| `prompt` | text, required | Prompt: What you want, and the data it is about. Everything here is sent to the model, so point at the values you need and not at the whole payload. |
| `output` | "text" or "fields", default `"text"` | Output: Text answers one piece of writing. Fields turns the answer into a typed object, the way Extract fields does. |
| `fields` | list of {name: text, description?: text, type?: "text" or "number" or "boolean" or "date" or "text-list" or "number-list", required?: true/false}, default `[]` | Fields: What you want back, and a sentence saying where to find each one — that sentence is most of what makes the answer right. — used when `output` is "fields" |
| `creativity` | number, optional | Creativity: 0 to 1. Low keeps the answer close to what you asked for; high lets the model wander, which is rarely what a flow wants. OpenAI's reasoning models — GPT-5 and later, and the o-series — ignore it; GPT-4.1 and GPT-4o honour it. |
| `maxLength` | number, default `1024` | Maximum length: In tokens — roughly three quarters of a word each. The model stops here, so a ceiling that is too low cuts the answer off mid-sentence. On an OpenAI reasoning model the reasoning gets its own room on top of this number, so it caps the answer alone. |
| `reasoning` | "default" or "low" or "medium" or "high", default `"default"` | Reasoning effort: How much an OpenAI reasoning model — GPT-5 and later, and the o-series — reasons before it answers. Model default leaves it to the model; GPT-5.5 and GPT-5.6 reason at medium unless told otherwise. GPT-4.1 and GPT-4o do not reason and refuse any level, so leave them on Model default. Reasoning is billed by OpenAI to your account as output tokens, gets its own room on top of the answer's output ceiling (Maximum length, where the step has one), and makes the step slower. |

Output: `text` string — Text · more fields follow from the config.

```json
{"type":"openai.generate","version":1,"label":"Generate text","config":{"task":"Custom","output":"text","fields":[],"maxLength":1024,"reasoning":"default","connection":"<connection id>","model":"<model>","prompt":"<prompt>"}}
```


---

## File: references/nodes/otys.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# OTYS nodes (`otys.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `otys.attachFile@1` — OTYS — Attach file to dossier

Attaches an uploaded OTYS file to a record's dossier.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `entity` | "candidate" or "relation" or "relationContact" or "vacancy" or "task" or "salesTrajectory" or "project", default `"candidate"` | Attach to |
| `recordOuid` | text, required | Record OUID |
| `fileOuid` | text, required | File OUID: The uploaded file's OUID — the Upload file node hands you `ouid`. |
| `documentTypeUid` | text, required | Document type: The OTYS document type — CV, motivation letter, contract. Read the list with a Search node over DocumentTypeService, or set one up in OTYS Go! under client setting GE91. |
| `subject` | text, required | Subject |

Output: `dossierId` string — Dossier id.

```json
{"type":"otys.attachFile","version":1,"label":"Attach file to dossier","config":{"entity":"candidate","connection":"<connection id>","recordOuid":"<record ouid>","fileOuid":"<file ouid>","documentTypeUid":"<document type>","subject":"<subject>"}}
```

## `otys.call@1` — OTYS — Call a service method

Calls any OTYS web-service method directly.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `service` | text, required | Service |
| `method` | text, required | Method: Any method the service publishes — setStatus, retrieveDuplicateInfo, createToken, wrapRequest. This node declares effect write, so the run never blind-retries it. |
| `params` | any JSON, optional | Parameters: Everything after the session token. An array is spread into the call's parameters; anything else is sent as a single one. |
| `language` | text, default `""` | Language: Sets the language for THIS call only, through LanguageService.wrapRequest. The connection's own language is never changed. |

Output: `result` any — OTYS answer · `method` string — The full method that was called.

```json
{"type":"otys.call","version":1,"label":"Call a service method","config":{"language":"","connection":"<connection id>","service":"<service>","method":"<method>"}}
```

## `otys.create@1` — OTYS — Create record

Creates a new record in one OTYS service.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `service` | text, required | Service |
| `data` | any JSON, optional | Fields: The record's fields as JSON, or one {{ }} expression that produces them. Only fields marked *insertable* at ows.otys.nl/info can be set on creation. |
| `language` | text, default `""` | Language: Sets the language for THIS call only, through LanguageService.wrapRequest. The connection's own language is never changed. |

Output: `id` string — New record OUID.

```json
{"type":"otys.create","version":1,"label":"Create record","config":{"language":"","connection":"<connection id>","service":"<service>"}}
```

## `otys.delete@1` — OTYS — Delete record

Deletes a record from one OTYS service.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `service` | text, required | Service |
| `ouid` | text, required | Record OUID |
| `language` | text, default `""` | Language: Sets the language for THIS call only, through LanguageService.wrapRequest. The connection's own language is never changed. |

Output: `deleted` boolean — Deleted.

```json
{"type":"otys.delete","version":1,"label":"Delete record","config":{"language":"","connection":"<connection id>","service":"<service>","ouid":"<record ouid>"}}
```

## `otys.downloadFile@1` — OTYS — Download file

Downloads one file from an OTYS dossier.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `fileOuid` | text, required | File OUID: The dossier row's `id_rec` — the List dossier files node hands it to you. |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"otys.downloadFile","version":1,"label":"Download file","config":{"connection":"<connection id>","fileOuid":"<file ouid>"}}
```

## `otys.get@1` — OTYS — Get record

Fetches one OTYS record by its OUID.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `service` | text, required | Service |
| `ouid` | text, required | Record OUID: The record's OTYS uid — a Search node hands you records[].uid. |
| `what` | any JSON, optional | Fields to fetch: The fields you want back, as OTYS's `what` object. |
| `language` | text, default `""` | Language: Sets the language for THIS call only, through LanguageService.wrapRequest. The connection's own language is never changed. |

Output: `record` any — Record.

```json
{"type":"otys.get","version":1,"label":"Get record","config":{"language":"","connection":"<connection id>","service":"<service>","ouid":"<record ouid>"}}
```

## `otys.getOptions@1` — OTYS — Get option lists

Reads the ids and labels behind OTYS dropdown fields.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `service` | text, required | Service |
| `fields` | text, required | Fields: The option-list fields to read, comma-separated — e.g. statusId, sourceId. |

Output: `options` object — Options by field.

```json
{"type":"otys.getOptions","version":1,"label":"Get option lists","config":{"connection":"<connection id>","service":"<service>","fields":"<fields>"}}
```

## `otys.listFiles@2` — OTYS — List dossier files

Lists the files in an OTYS record's dossier.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `entity` | "candidate" or "relation" or "relationContact" or "vacancy" or "task" or "salesTrajectory" or "project", default `"candidate"` | Entity |
| `recordOuid` | text, required | Record OUID |
| `limit` | number, default `100` | Files per call: 1 to 200 — OTYS never returns more than 200, whatever you ask. |
| `offset` | number, default `0` | Skip files |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on offset, and refuses past 10,000 files. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `files` array — Files · `fileCount` number — Files in this answer · `total` number — How many OTYS counted · `hasMore` boolean — More files are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"otys.listFiles","version":2,"label":"List dossier files","config":{"entity":"candidate","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>","recordOuid":"<record ouid>"}}
```

### Older version `otys.listFiles@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `entity` | "candidate" or "relation" or "relationContact" or "vacancy" or "task" or "salesTrajectory" or "project", default `"candidate"` | Entity |
| `recordOuid` | text, required | Record OUID |
| `limit` | number, default `100` | Files per call: 1 to 200 — OTYS never returns more than 200, whatever you ask. |
| `offset` | number, default `0` | Skip files |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on offset, and refuses past 10,000 files. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"entity":"candidate","limit":100,"offset":0,"returnAll":false,"connection":"<connection id>","recordOuid":"<record ouid>"}
{"entity":"candidate","limit":100,"offset":0,"returnAll":false,"cursor":"","connection":"<connection id>","recordOuid":"<record ouid>"}
```

## `otys.search@2` — OTYS — Search records

Searches one OTYS service and returns matching records.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `service` | text, required | Service: The OTYS data service, as spelled at ows.otys.nl/info — CandidateService, VacancyService, RelationService. |
| `what` | any JSON, optional | Fields to fetch: The fields you want back, as OTYS's `what` object: a 1 for each field. Ask for what you need — OTYS gets slower and less stable when you take its defaults. |
| `condition` | any JSON, optional | Filter: OTYS's condition tree. {"type":"COND","field":…,"op":…,"param":…}, or {"type":"AND","items":[…]}. Operators: EQ NE GE GT LE LT T F LIKE NLIKE. Leave empty for every record. |
| `sort` | any JSON, optional | Sort by: {"Person.lastName":"ASC"} — OTYS gives no order guarantee across pages without one. |
| `limit` | number, default `100` | Records per call: 1 to 200 — OTYS never returns more than 200, whatever you ask. |
| `offset` | number, default `0` | Skip records |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on offset until the total is read, and refuses past 10,000 records rather than answering a short list. |
| `language` | text, default `""` | Language: Sets the language for THIS call only, through LanguageService.wrapRequest. The connection's own language is never changed. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `records` array — Records · `recordCount` number — Records in this answer · `total` number — How many OTYS counted · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"otys.search","version":2,"label":"Search records","config":{"limit":100,"offset":0,"returnAll":false,"language":"","cursor":"","connection":"<connection id>","service":"<service>"}}
```

### Older version `otys.search@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `service` | text, required | Service: The OTYS data service, as spelled at ows.otys.nl/info — CandidateService, VacancyService, RelationService. |
| `what` | any JSON, optional | Fields to fetch: The fields you want back, as OTYS's `what` object: a 1 for each field. Ask for what you need — OTYS gets slower and less stable when you take its defaults. |
| `condition` | any JSON, optional | Filter: OTYS's condition tree. {"type":"COND","field":…,"op":…,"param":…}, or {"type":"AND","items":[…]}. Operators: EQ NE GE GT LE LT T F LIKE NLIKE. Leave empty for every record. |
| `sort` | any JSON, optional | Sort by: {"Person.lastName":"ASC"} — OTYS gives no order guarantee across pages without one. |
| `limit` | number, default `100` | Records per call: 1 to 200 — OTYS never returns more than 200, whatever you ask. |
| `offset` | number, default `0` | Skip records |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on offset until the total is read, and refuses past 10,000 records rather than answering a short list. |
| `language` | text, default `""` | Language: Sets the language for THIS call only, through LanguageService.wrapRequest. The connection's own language is never changed. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":100,"offset":0,"returnAll":false,"language":"","connection":"<connection id>","service":"<service>"}
{"limit":100,"offset":0,"returnAll":false,"language":"","cursor":"","connection":"<connection id>","service":"<service>"}
```

## `otys.update@1` — OTYS — Update record

Changes fields on an existing OTYS record.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `service` | text, required | Service |
| `ouid` | text, required | Record OUID |
| `data` | any JSON, optional | Fields: Only the fields you name change; everything else stays as it is. |
| `language` | text, default `""` | Language: Sets the language for THIS call only, through LanguageService.wrapRequest. The connection's own language is never changed. |

Output: `result` any — OTYS answer.

```json
{"type":"otys.update","version":1,"label":"Update record","config":{"language":"","connection":"<connection id>","service":"<service>","ouid":"<record ouid>"}}
```

## `otys.uploadFile@1` — OTYS — Upload file

Uploads a file to OTYS and returns its OUID.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `otys`) |
| `content` | text, required | File contents: Base64 of the file — {{ file("cv.pdf").base64 }}. — its `{{ }}` must give a string |
| `name` | text, required | File name |
| `contentType` | text, default `"application/octet-stream"` | Content type |

Output: `ouid` string — File OUID · `name` string — File name · `mimeType` string — Content type · `size` number — Size in bytes.

```json
{"type":"otys.uploadFile","version":1,"label":"Upload file","config":{"contentType":"application/octet-stream","connection":"<connection id>","content":"<file contents>","name":"<file name>"}}
```


---

## File: references/nodes/personio.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Personio nodes (`personio.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `personio.createAbsence@1` — Personio — Create absence

Books leave, sickness or another absence for a Personio employee.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `personId` | text, required | Employee id |
| `absenceTypeId` | text, required | Absence type id: The List absence types node gives you the id. |
| `startsFrom` | text, required | Starts: A date and time WITHOUT a timezone offset — Personio reads it in the employee's own timezone. |
| `startsFromHalf` | "" or "FIRST_HALF" or "SECOND_HALF", default `""` | First day: Half days only count when the absence type allows them. |
| `endsAt` | text, default `""` | Ends: Leave empty for an absence with no end date yet. |
| `endsAtHalf` | "" or "FIRST_HALF" or "SECOND_HALF", default `""` | Last day |
| `comment` | text, default `""` | Comment |
| `substitutePersonId` | text, default `""` | Substitute id: Only if this absence type's policy allows a substitute. |
| `skipApproval` | true/false, default `false` | Skip approval: On, Personio books the absence without asking a manager. Personio does not document this on booking, so leave it off unless your tenant accepts it. |

Output: `absenceId` string — Absence id · `absence` object — Absence, as Personio answered it.

```json
{"type":"personio.createAbsence","version":1,"label":"Create absence","config":{"startsFromHalf":"","endsAt":"","endsAtHalf":"","comment":"","substitutePersonId":"","skipApproval":false,"connection":"<connection id>","personId":"<employee id>","absenceTypeId":"<absence type id>","startsFrom":"<starts>"}}
```

## `personio.createApplication@1` — Personio — Create application

Posts an application into a Personio job pipeline.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The Personio Recruiting connection — the recruiting token and company id, not the employee-data client id. — a connection id from `requires.connections` (provider `personio-recruiting`) |
| `firstName` | text, required | First name — its `{{ }}` must give a string |
| `lastName` | text, required | Last name — its `{{ }}` must give a string |
| `email` | text, required | E-mail — its `{{ }}` must give a string |
| `jobPositionId` | number, required | Job id: The List recruiting jobs node gives you the id. The posting has to be published at the moment you post. |
| `recruitingChannelId` | number, default `0` | Channel id: Which source this application came in through. Leave at 0 for none. |
| `externalPostingId` | text, default `""` | External posting id: The id of the job board posting the applicant answered. |
| `message` | text, default `""` | Message: The applicant's own motivation, as they wrote it. |
| `applicationDate` | text, default `""` | Application date: YYYY-MM-DD, and never in the future. Leave empty for today. |
| `phaseType` | "" or "system" or "custom", default `""` | Starting phase |
| `phaseId` | text, default `""` | Phase id: For a fixed phase: unassigned, rejected, withdrawn, offer or accepted. An unknown phase silently becomes unassigned. — used when `phaseType` is not "" |
| `tags` | text, default `""` | Tags: Comma-separated. A tag that does not exist yet is created. |
| `files` | list of {uuid: text, originalFilename: text, category: "cv" or "cover-letter" or "employment-reference" or "certificate" or "work-sample" or "other"}, default `[]` | Files: The document id comes from the Upload application document node. These rows are literal — to pass an id from an earlier node, use the field below. |
| `filesJson` | list of any JSON, default `[]` | Files from an earlier node: An array of { uuid, original_filename, category }. Use this when the id comes from an earlier node; it replaces the rows above. |
| `attributes` | object of text, default `{}` | Applicant attributes: Personio's own ids are birthday, gender, location, phone, available_from and salary_expectations. Your own look like custom_attribute_123 — find them under Settings → Recruiting → Applicant. |

Output: `created` boolean — Personio accepted the application.

```json
{"type":"personio.createApplication","version":1,"label":"Create application","config":{"recruitingChannelId":0,"externalPostingId":"","message":"","applicationDate":"","phaseType":"","phaseId":"","tags":"","files":[],"filesJson":[],"attributes":{},"connection":"<connection id>","firstName":"<first name>","lastName":"<last name>","email":"<e-mail>","jobPositionId":1}}
```

## `personio.createAttendance@1` — Personio — Create attendance

Records a work or break period for a Personio employee.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `personId` | text, required | Employee id |
| `periodType` | "WORK" or "BREAK", default `"WORK"` | Kind of period |
| `start` | text, required | Starts: A date and time without a timezone offset. |
| `end` | text, default `""` | Ends: Leave empty for a period that is still running. |
| `comment` | text, default `""` | Comment |
| `projectId` | text, default `""` | Project id: The project has to be Active. A break cannot carry a project. — used when `periodType` is "WORK" |
| `skipApproval` | true/false, default `false` | Skip approval: Off, Personio files the period as Pending and asks a manager. On, it is confirmed immediately. |

Output: `attendanceId` string — Attendance id · `affectedPeriods` array — Every period Personio created or changed for this day · `affectedCount` number — Periods Personio touched.

```json
{"type":"personio.createAttendance","version":1,"label":"Create attendance","config":{"periodType":"WORK","end":"","comment":"","projectId":"","skipApproval":false,"connection":"<connection id>","personId":"<employee id>","start":"<starts>"}}
```

## `personio.createCompensation@1` — Personio — Create compensation

Adds a salary, hourly rate or recurring payment to a Personio employee.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: This node needs a credential with both the compensations read and write permissions. — a connection id from `requires.connections` (provider `personio`) |
| `personId` | text, required | Employee id |
| `compensationTypeId` | text, required | Compensation type id: The List compensation types node gives you the id. |
| `value` | number, required | Amount |
| `currency` | text, default `"EUR"` | Currency: A three-letter ISO code. Personio checks it. |
| `effectiveFrom` | text, required | Effective from |
| `interval` | "" or "MONTHLY" or "QUARTERLY" or "HALF-YEARLY" or "YEARLY", default `""` | Interval: Recurring payments need one; a one-off payment ignores it. |
| `comment` | text, default `""` | Comment |

Output: `compensationId` string — Compensation id · `compensation` object — Row, as Personio answered it.

```json
{"type":"personio.createCompensation","version":1,"label":"Create compensation","config":{"currency":"EUR","interval":"","comment":"","connection":"<connection id>","personId":"<employee id>","compensationTypeId":"<compensation type id>","value":1,"effectiveFrom":"<effective from>"}}
```

## `personio.createPerson@1` — Personio — Create employee

Creates a Personio employee and their first employment.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `firstName` | text, required | First name — its `{{ }}` must give a string |
| `lastName` | text, required | Last name — its `{{ }}` must give a string |
| `email` | text, default `""` | E-mail |
| `preferredName` | text, default `""` | Preferred name |
| `gender` | "" or "MALE" or "FEMALE" or "DIVERSE" or "UNKNOWN" or "UNDEFINED", default `""` | Gender |
| `languageCode` | "" or "en" or "de" or "es" or "fr" or "nl" or "it" or "sv" or "fi", default `""` | Language |
| `customAttributes` | object of text, default `{}` | Custom attributes: Keyed by attribute id — dynamic_24407, not Title. A Get employee answer shows each id beside its label. |
| `employmentStartDate` | text, default `""` | Start date |
| `employmentStatus` | "" or "ACTIVE" or "INACTIVE" or "ONBOARDING" or "LEAVE", default `""` | Employment status |
| `employmentType` | "" or "INTERNAL" or "EXTERNAL", default `""` | Employment type |
| `positionTitle` | text, default `""` | Job title |
| `supervisorId` | text, default `""` | Manager id: The Personio person id of this employee's manager. |
| `officeId` | text, default `""` | Office id: The List org data node answers the offices and their ids. |
| `legalEntityId` | text, default `""` | Legal entity id |
| `jobId` | text, default `""` | Job id: The job from Personio's own job catalogue, not the title above. |
| `weeklyWorkingHours` | number, optional | Weekly hours |
| `fullTimeWeeklyWorkingHours` | number, optional | Full-time weekly hours: What a full week is for this role — the two together are the part-time factor. |
| `contractEndDate` | text, default `""` | Contract end date |
| `probationEndDate` | text, default `""` | Probation end date |
| `orgUnits` | list of {type: "department" or "team", id: text}, default `[]` | Departments and teams: Literal values — a value from an earlier node belongs in an expression field, not in these rows. |
| `costCenters` | list of {id: text, weight: text}, default `[]` | Cost centres: Weights are percentages and must total 100. |

Output: `personId` string — Employee id · `employmentIds` array — Employment ids · `person` object — Employee, as Personio answered it.

```json
{"type":"personio.createPerson","version":1,"label":"Create employee","config":{"email":"","preferredName":"","gender":"","languageCode":"","customAttributes":{},"employmentStartDate":"","employmentStatus":"","employmentType":"","positionTitle":"","supervisorId":"","officeId":"","legalEntityId":"","jobId":"","contractEndDate":"","probationEndDate":"","orgUnits":[],"costCenters":[],"connection":"<connection id>","firstName":"<first name>","lastName":"<last name>"}}
```

## `personio.deleteAbsence@1` — Personio — Delete absence

Withdraws a Personio absence period.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `absenceId` | text, required | Absence id |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — Personio had no such absence any more · `absenceId` string — Absence id.

```json
{"type":"personio.deleteAbsence","version":1,"label":"Delete absence","config":{"connection":"<connection id>","absenceId":"<absence id>"}}
```

## `personio.deleteAttendance@1` — Personio — Delete attendance

Removes a recorded Personio work or break period.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `attendanceId` | text, required | Attendance id |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — Personio had no such period any more · `attendanceId` string — Attendance id.

```json
{"type":"personio.deleteAttendance","version":1,"label":"Delete attendance","config":{"connection":"<connection id>","attendanceId":"<attendance id>"}}
```

## `personio.deletePerson@1` — Personio — Delete employee

Deletes a Personio employee and everything filed on them.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `personId` | text, required | Employee id: The Personio person id. Deleting is permanent. |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — Personio had no such employee any more · `personId` string — Employee id.

```json
{"type":"personio.deletePerson","version":1,"label":"Delete employee","config":{"connection":"<connection id>","personId":"<employee id>"}}
```

## `personio.downloadDocument@1` — Personio — Download document

Downloads a Personio document's bytes as base64.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `documentId` | text, required | Document id: The List documents node gives you the id. |
| `fileName` | text, default `""` | File name: The name to carry the bytes under — the List documents answer has one. Personio does not send a name with the file. |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"personio.downloadDocument","version":1,"label":"Download document","config":{"fileName":"","connection":"<connection id>","documentId":"<document id>"}}
```

## `personio.getAbsence@1` — Personio — Get absence

Reads one Personio absence period by its id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `absenceId` | text, required | Absence id: The List absences node gives you the id. |

Output: `absence` object — Absence period.

```json
{"type":"personio.getAbsence","version":1,"label":"Get absence","config":{"connection":"<connection id>","absenceId":"<absence id>"}}
```

## `personio.getApplication@1` — Personio — Get application

Reads one Personio recruiting application by its id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `applicationId` | text, required | Application id: The List applications node gives you the id. |

Output: `application` object — Application.

```json
{"type":"personio.getApplication","version":1,"label":"Get application","config":{"connection":"<connection id>","applicationId":"<application id>"}}
```

## `personio.getPerson@1` — Personio — Get employee

Reads one Personio employee, with every attribute the integration may see.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `personId` | text, required | Employee id: The Personio person id — the List employees node gives you one. |

Output: `person` object — Employee · `person.custom_attributes` array — Custom attributes, each with the id a write uses.

```json
{"type":"personio.getPerson","version":1,"label":"Get employee","config":{"connection":"<connection id>","personId":"<employee id>"}}
```

## `personio.listAbsenceTypes@1` — Personio — List absence types

Lists the leave and absence types this Personio account books against.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `100` | Types per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 types rather than answering a short list. |

Output: `absenceTypes` array — Absence types · `absenceTypeCount` number — Types in this answer · `hasMore` boolean — More types are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listAbsenceTypes","version":1,"label":"List absence types","config":{"cursor":"","limit":100,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.listAbsences@1` — Personio — List absences

Lists Personio absence periods for a person, a type or a date range.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `personIds` | text, default `""` | Employee ids: One or more Personio person ids, comma-separated. |
| `absenceTypeIds` | text, default `""` | Absence type ids: The List absence types node gives you these ids. |
| `ids` | text, default `""` | Absence ids |
| `startsFrom` | text, default `""` | Starting on or after: A date range matches every absence that OVERLAPS it, not only absences entirely inside it. |
| `startsUntil` | text, default `""` | Starting on or before |
| `endsFrom` | text, default `""` | Ending on or after |
| `endsUntil` | text, default `""` | Ending on or before |
| `updatedAfter` | text, default `""` | Changed after |
| `updatedBefore` | text, default `""` | Changed before |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `100` | Absences per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 absences rather than answering a short list. |

Output: `absences` array — Absence periods · `absenceCount` number — Absences in this answer · `hasMore` boolean — More absences are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listAbsences","version":1,"label":"List absences","config":{"personIds":"","absenceTypeIds":"","ids":"","startsFrom":"","startsUntil":"","endsFrom":"","endsUntil":"","updatedAfter":"","updatedBefore":"","cursor":"","limit":100,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.listApplicationStages@2` — Personio — List application stages

Lists when a Personio application entered each pipeline stage.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `applicationId` | text, required | Application id |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 stages rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `transitions` array — Stage transitions, latest first · `transitionCount` number — Transitions in this answer · `hasMore` boolean — More transitions are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listApplicationStages","version":2,"label":"List application stages","config":{"returnAll":false,"cursor":"","connection":"<connection id>","applicationId":"<application id>"}}
```

### Older version `personio.listApplicationStages@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `applicationId` | text, required | Application id |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 stages rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"returnAll":false,"connection":"<connection id>","applicationId":"<application id>"}
{"returnAll":false,"cursor":"","connection":"<connection id>","applicationId":"<application id>"}
```

## `personio.listApplications@1` — Personio — List applications

Lists Personio recruiting applications, newest change first.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The ordinary Personio connection. Reading the pipeline uses the employee-data credential, not the recruiting token. — a connection id from `requires.connections` (provider `personio`) |
| `candidateEmail` | text, default `""` | Candidate e-mail |
| `updatedAfter` | text, default `""` | Changed after: Personio takes a change filter in one direction only. |
| `updatedBefore` | text, default `""` | Changed before |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `100` | Applications per call: 1 to 200. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 applications rather than answering a short list. |

Output: `applications` array — Applications · `applicationCount` number — Applications in this answer · `hasMore` boolean — More applications are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listApplications","version":1,"label":"List applications","config":{"candidateEmail":"","updatedAfter":"","updatedBefore":"","cursor":"","limit":100,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.listAttendances@1` — Personio — List attendances

Lists Personio work and break periods for people, projects or a date range.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `personIds` | text, default `""` | Employee ids: One or more Personio person ids, comma-separated. |
| `ids` | text, default `""` | Attendance ids |
| `projectIds` | text, default `""` | Project ids: The List projects node gives you these ids. |
| `startFrom` | text, default `""` | Starting on or after |
| `startUntil` | text, default `""` | Starting on or before |
| `endFrom` | text, default `""` | Ending on or after |
| `endUntil` | text, default `""` | Ending on or before |
| `attributionFrom` | text, default `""` | Counted from: The day Personio books the hours on, which is not always the day they started. |
| `attributionUntil` | text, default `""` | Counted until |
| `updatedAfter` | text, default `""` | Changed after |
| `updatedBefore` | text, default `""` | Changed before |
| `status` | "" or "PENDING" or "CONFIRMED" or "REJECTED", default `""` | Approval status |
| `sort` | text, default `""` | Sort by: Comma-separated; a leading minus sorts descending. Allowed: person.id, start.date_time, end.date_time, updated_at, status. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `100` | Periods per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 periods rather than answering a short list. |

Output: `attendances` array — Work and break periods · `attendanceCount` number — Periods in this answer · `hasMore` boolean — More periods are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listAttendances","version":1,"label":"List attendances","config":{"personIds":"","ids":"","projectIds":"","startFrom":"","startUntil":"","endFrom":"","endUntil":"","attributionFrom":"","attributionUntil":"","updatedAfter":"","updatedBefore":"","status":"","sort":"","cursor":"","limit":100,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.listCandidates@1` — Personio — List candidates

Lists the people in your Personio talent pool, newest change first.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The ordinary Personio connection, not the recruiting token. — a connection id from `requires.connections` (provider `personio`) |
| `email` | text, default `""` | E-mail |
| `updatedAfter` | text, default `""` | Changed after: Personio takes a change filter in one direction only. This answer carries no custom attributes and no tags. |
| `updatedBefore` | text, default `""` | Changed before |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `100` | Candidates per call: 1 to 200. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 candidates rather than answering a short list. |

Output: `candidates` array — Candidates · `candidateCount` number — Candidates in this answer · `hasMore` boolean — More candidates are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listCandidates","version":1,"label":"List candidates","config":{"email":"","updatedAfter":"","updatedBefore":"","cursor":"","limit":100,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.listCompensationTypes@1` — Personio — List compensation types

Lists the compensation types a Personio salary row can be filed under.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `100` | Types per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 types rather than answering a short list. |

Output: `compensationTypes` array — Compensation types · `compensationTypeCount` number — Types in this answer · `hasMore` boolean — More types are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listCompensationTypes","version":1,"label":"List compensation types","config":{"cursor":"","limit":100,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.listCompensations@1` — Personio — List compensations

Lists Personio salary and bonus rows for one month.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `startDate` | text, default `""` | From: Leave both dates empty for the current month. |
| `endDate` | text, default `""` | Until: Personio answers at most one month at a time. |
| `personIds` | text, default `""` | Employee ids |
| `legalEntityIds` | text, default `""` | Legal entity ids |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `100` | Rows per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 rows rather than answering a short list. |

Output: `compensations` array — Salary and bonus rows · `compensationCount` number — Rows in this answer · `hasMore` boolean — More rows are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listCompensations","version":1,"label":"List compensations","config":{"startDate":"","endDate":"","personIds":"","legalEntityIds":"","cursor":"","limit":100,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.listDocuments@1` — Personio — List documents

Lists the documents filed on one Personio employee.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `ownerId` | text, required | Employee id: Required — Personio only answers per employee. The person id is the owner id. |
| `categoryId` | text, default `""` | Category id |
| `createdFrom` | text, default `""` | Filed on or after |
| `createdBefore` | text, default `""` | Filed before |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `100` | Documents per call: 1 to 200. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 documents rather than answering a short list. |

Output: `documents` array — Documents · `documentCount` number — Documents in this answer · `hasMore` boolean — More documents are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listDocuments","version":1,"label":"List documents","config":{"categoryId":"","createdFrom":"","createdBefore":"","cursor":"","limit":100,"returnAll":false,"connection":"<connection id>","ownerId":"<employee id>"}}
```

## `personio.listEmployments@1` — Personio — List employments

Lists one Personio employee's contracts, newest first.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `personId` | text, required | Employee id: The Personio person id whose contracts you want. |
| `ids` | text, default `""` | Employment ids: One or more employment ids, comma-separated. |
| `updatedAfter` | text, default `""` | Changed after |
| `updatedBefore` | text, default `""` | Changed before |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `50` | Contracts per call: 1 to 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 contracts rather than answering a short list. |

Output: `employments` array — Contracts · `employmentCount` number — Contracts in this answer · `hasMore` boolean — More contracts are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listEmployments","version":1,"label":"List employments","config":{"ids":"","updatedAfter":"","updatedBefore":"","cursor":"","limit":50,"returnAll":false,"connection":"<connection id>","personId":"<employee id>"}}
```

## `personio.listOrgData@1` — Personio — List org data

Lists Personio departments, teams, legal entities, cost centres, offices, jobs or salary bands.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `resource` | "orgUnits" or "legalEntities" or "costCenters" or "workplaces" or "jobs" or "salaryBands", default `"orgUnits"` | What to list |
| `orgUnitType` | "department" or "team", default `"department"` | Kind of unit: Personio answers one kind at a time and needs to be told which. — used when `resource` is "orgUnits" |
| `includeParentChain` | true/false, default `false` | Include the chain of parents — used when `resource` is "orgUnits" |
| `countries` | text, default `""` | Countries: Two-letter country codes, comma-separated. — used when `resource` is "legalEntities" |
| `ids` | text, default `""` | Ids |
| `names` | text, default `""` | Names: Exact names, comma-separated. — used when `resource` is not "legalEntities" |
| `sort` | text, default `""` | Sort by: A leading minus sorts descending. — used when `resource` is not "legalEntities" |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `100` | Rows per call: 1 to 100 — every one of these six endpoints caps there. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 rows rather than answering a short list. |

Output: `items` array — Rows · `itemCount` number — Rows in this answer · `hasMore` boolean — More rows are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listOrgData","version":1,"label":"List org data","config":{"resource":"orgUnits","orgUnitType":"department","includeParentChain":false,"countries":"","ids":"","names":"","sort":"","cursor":"","limit":100,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.listPersons@1` — Personio — List employees

Lists Personio employees, with filters on name, e-mail, status and change date.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `ids` | text, default `""` | Employee ids: One or more Personio person ids, comma-separated. |
| `email` | text, default `""` | E-mail |
| `firstName` | text, default `""` | First name: Exact match — Personio does not search on part of a name. |
| `lastName` | text, default `""` | Last name |
| `preferredName` | text, default `""` | Preferred name |
| `status` | "" or "ACTIVE" or "INACTIVE", default `""` | Status |
| `updatedAfter` | text, default `""` | Changed after: The delta filter to build a sync on. |
| `updatedBefore` | text, default `""` | Changed before |
| `createdAfter` | text, default `""` | Created after |
| `cursor` | text, default `""` | Cursor: Reads the page after a previous run: paste that run's Next cursor. Leave it empty for the first page. |
| `limit` | number, default `50` | Employees per call: 1 to 50. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 employees rather than answering a short list. |

Output: `persons` array — Employees · `personCount` number — Employees in this answer · `hasMore` boolean — More employees are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listPersons","version":1,"label":"List employees","config":{"ids":"","email":"","firstName":"","lastName":"","preferredName":"","status":"","updatedAfter":"","updatedBefore":"","createdAfter":"","cursor":"","limit":50,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.listProjects@1` — Personio — List projects

Lists the Personio projects that hours can be booked against.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `ids` | text, default `""` | Project ids |
| `names` | text, default `""` | Names: Exact names, comma-separated. |
| `projectCodes` | text, default `""` | Project codes |
| `parentProjectIds` | text, default `""` | Parent project ids: Answers the subprojects of these projects. |
| `status` | "" or "ACTIVE" or "ARCHIVED", default `""` | Status |
| `topLevelOnly` | true/false, default `false` | Top-level projects only |
| `include` | text, default `""` | Extra fields: Comma-separated. Personio leaves these out unless you ask for them. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `200` | Projects per call: 1 to 200. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 projects rather than answering a short list. |

Output: `projects` array — Projects · `projectCount` number — Projects in this answer · `hasMore` boolean — More projects are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listProjects","version":1,"label":"List projects","config":{"ids":"","names":"","projectCodes":"","parentProjectIds":"","status":"","topLevelOnly":false,"include":"","cursor":"","limit":200,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.listRecruitingJobs@1` — Personio — List recruiting jobs

Lists the job openings your Personio pipeline is hiring for.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The ordinary Personio connection, not the recruiting token. — a connection id from `requires.connections` (provider `personio`) |
| `updatedAfter` | text, default `""` | Changed after: Personio takes a change filter in one direction only. |
| `updatedBefore` | text, default `""` | Changed before |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |
| `limit` | number, default `100` | Jobs per call: 1 to 200. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Personio's next-page cursor, and refuses past 5,000 jobs rather than answering a short list. |

Output: `jobs` array — Job openings · `jobCount` number — Jobs in this answer · `hasMore` boolean — More jobs are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"personio.listRecruitingJobs","version":1,"label":"List recruiting jobs","config":{"updatedAfter":"","updatedBefore":"","cursor":"","limit":100,"returnAll":false,"connection":"<connection id>"}}
```

## `personio.updateAbsence@1` — Personio — Update absence

Moves a Personio absence period in time, or changes its comment.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `absenceId` | text, required | Absence id |
| `startsFrom` | text, default `""` | Starts: A date and time without a timezone offset. Leave empty to keep it as it is. |
| `startsFromHalf` | "" or "FIRST_HALF" or "SECOND_HALF", default `""` | First day |
| `endsAt` | text, default `""` | Ends |
| `endsAtHalf` | "" or "FIRST_HALF" or "SECOND_HALF", default `""` | Last day |
| `comment` | text, default `""` | Comment |
| `skipApproval` | true/false, default `false` | Skip approval: On, Personio applies the change without asking a manager. |

Output: `absence` object — Absence, as Personio answered it · `updated` boolean — Personio accepted the change.

```json
{"type":"personio.updateAbsence","version":1,"label":"Update absence","config":{"startsFrom":"","startsFromHalf":"","endsAt":"","endsAtHalf":"","comment":"","skipApproval":false,"connection":"<connection id>","absenceId":"<absence id>"}}
```

## `personio.updateAttendance@1` — Personio — Update attendance

Changes a recorded Personio work period's times, comment or project.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `attendanceId` | text, required | Attendance id: The List attendances node gives you the id. |
| `start` | text, default `""` | Starts: A date and time without a timezone offset. Leave empty to keep it as it is. |
| `end` | text, default `""` | Ends |
| `comment` | text, default `""` | Comment |
| `projectId` | text, default `""` | Project id |
| `skipApproval` | true/false, default `false` | Skip approval: On, Personio applies the change without asking a manager. |

Output: `attendance` object — Period, as Personio answered it · `updated` boolean — Personio accepted the change.

```json
{"type":"personio.updateAttendance","version":1,"label":"Update attendance","config":{"start":"","end":"","comment":"","projectId":"","skipApproval":false,"connection":"<connection id>","attendanceId":"<attendance id>"}}
```

## `personio.updateEmployment@1` — Personio — Update employment

Changes a contract's position, manager, hours, dates or cost centres.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `personId` | text, required | Employee id |
| `employmentId` | text, required | Employment id: The List employments node gives you the id. |
| `status` | "" or "ACTIVE" or "INACTIVE" or "ONBOARDING" or "LEAVE", default `""` | Status |
| `type` | "" or "INTERNAL" or "EXTERNAL", default `""` | Employment type |
| `positionTitle` | text, default `""` | Job title |
| `supervisorId` | text, default `""` | Manager id: The Personio person id of the new manager. |
| `officeId` | text, default `""` | Office id |
| `legalEntityId` | text, default `""` | Legal entity id |
| `jobId` | text, default `""` | Job id |
| `employmentStartDate` | text, default `""` | Start date |
| `contractEndDate` | text, default `""` | Contract end date |
| `probationEndDate` | text, default `""` | Probation end date: Personio takes an end date or a length, never both. |
| `probationPeriodLength` | number, default `0` | Probation length in months: Leave at 0 to use the end date above instead. |
| `weeklyWorkingHours` | number, optional | Weekly hours |
| `fullTimeWeeklyWorkingHours` | number, optional | Full-time weekly hours |
| `orgUnits` | list of {type: "department" or "team", id: text}, default `[]` | Departments and teams: Sending these REPLACES the units this contract has. |
| `costCenters` | list of {id: text, weight: text}, default `[]` | Cost centres: Weights are percentages and must total 100. |

Output: `employment` object — Contract, as Personio answered it · `updated` boolean — Personio accepted the change.

```json
{"type":"personio.updateEmployment","version":1,"label":"Update employment","config":{"status":"","type":"","positionTitle":"","supervisorId":"","officeId":"","legalEntityId":"","jobId":"","employmentStartDate":"","contractEndDate":"","probationEndDate":"","probationPeriodLength":0,"orgUnits":[],"costCenters":[],"connection":"<connection id>","personId":"<employee id>","employmentId":"<employment id>"}}
```

## `personio.updatePerson@1` — Personio — Update employee

Changes a Personio employee's name, e-mail, language or custom attributes.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `personio`) |
| `personId` | text, required | Employee id: The Personio person id of the employee to change. |
| `firstName` | text, default `""` | First name |
| `lastName` | text, default `""` | Last name |
| `preferredName` | text, default `""` | Preferred name |
| `email` | text, default `""` | E-mail |
| `gender` | "" or "MALE" or "FEMALE" or "DIVERSE" or "UNKNOWN" or "UNDEFINED", default `""` | Gender |
| `languageCode` | "" or "en" or "de" or "es" or "fr" or "nl" or "it" or "sv" or "fi", default `""` | Language |
| `customAttributes` | object of text, default `{}` | Custom attributes: Keyed by attribute id — dynamic_24407, not Title. Only the attributes you list here are changed. |

Output: `person` object — Employee, as Personio answered it · `updated` boolean — Personio accepted the change.

```json
{"type":"personio.updatePerson","version":1,"label":"Update employee","config":{"firstName":"","lastName":"","preferredName":"","email":"","gender":"","languageCode":"","customAttributes":{},"connection":"<connection id>","personId":"<employee id>"}}
```

## `personio.uploadApplicationDocument@1` — Personio — Upload application document

Uploads a CV or letter, and answers the id an application attaches it by.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The Personio Recruiting connection — the recruiting token and company id, not the employee-data client id. — a connection id from `requires.connections` (provider `personio-recruiting`) |
| `fileName` | text, required | File name: The name Personio stores it under, extension included. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("name").base64 }}. Personio takes up to 20 MB. — its `{{ }}` must give a string |

Output: `uuid` string — Document id, for Create application · `size` number — Size in bytes · `mimetype` string — Content type · `originalFilename` string — File name · `extension` string — Extension.

```json
{"type":"personio.uploadApplicationDocument","version":1,"label":"Upload application document","config":{"connection":"<connection id>","fileName":"<file name>","contentBase64":"<file contents>"}}
```


---

## File: references/nodes/pipedrive.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Pipedrive nodes (`pipedrive.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `pipedrive.addProductToDeal@3` — Pipedrive — Add product to deal

Puts a product line on a Pipedrive deal with its price and quantity.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `dealId` | text, required | Deal id — its `{{ }}` must give a string |
| `productId` | number, required | Product id: The List records node, set to Products, gives you the id. |
| `quantity` | number, default `1` | Quantity |
| `discount` | number, optional | Discount |
| `discountType` | "percentage" or "amount", default `"percentage"` | Discount is |
| `tax` | number, optional | Tax: Read the way the company's tax setting says — a percentage or an amount. |
| `productVariationId` | number, default `0` | Variation id: Leave at 0 for the product itself rather than one of its variations. |
| `comments` | text, default `""` | Comment |
| `itemPrice` | number, optional | Price per unit: Leave empty to use the product's own price in the deal's currency; 0 adds the line free. |

Output: `productAttachmentId` string — Product line id · `productAttachment` object — Product line.

```json
{"type":"pipedrive.addProductToDeal","version":3,"label":"Add product to deal","config":{"quantity":1,"discountType":"percentage","productVariationId":0,"comments":"","connection":"<connection id>","dealId":"<deal id>","productId":1}}
```

### Older version `pipedrive.addProductToDeal@1` — still runs; upgrade it to @3

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `dealId` | text, required | Deal id — its `{{ }}` must give a string |
| `productId` | number, required | Product id: The List records node, set to Products, gives you the id. |
| `quantity` | number, default `1` | Quantity |
| `discount` | number, default `0` | Discount |
| `discountType` | "percentage" or "amount", default `"percentage"` | Discount is |
| `tax` | number, default `0` | Tax: Read the way the company's tax setting says — a percentage or an amount. |
| `productVariationId` | number, default `0` | Variation id: Leave at 0 for the product itself rather than one of its variations. |
| `comments` | text, default `""` | Comment |
| `itemPrice` | number, default `0` | Price per unit: Leave at 0 to use the product's own price in the deal's currency. |

Upgrade @1 → @3: set `version` to 3 and replace the config — before, then after:
```json
{"quantity":1,"discount":0,"discountType":"percentage","tax":0,"productVariationId":0,"comments":"","itemPrice":0,"connection":"<connection id>","dealId":"<deal id>","productId":1}
{"quantity":1,"discountType":"percentage","productVariationId":0,"comments":"","discount":0,"tax":0,"itemPrice":0,"connection":"<connection id>","dealId":"<deal id>","productId":1}
```

### Older version `pipedrive.addProductToDeal@2` — still runs; upgrade it to @3

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `dealId` | text, required | Deal id — its `{{ }}` must give a string |
| `productId` | number, required | Product id: The List records node, set to Products, gives you the id. |
| `quantity` | number, default `1` | Quantity |
| `discount` | number, default `0` | Discount |
| `discountType` | "percentage" or "amount", default `"percentage"` | Discount is |
| `tax` | number, default `0` | Tax: Read the way the company's tax setting says — a percentage or an amount. |
| `productVariationId` | number, default `0` | Variation id: Leave at 0 for the product itself rather than one of its variations. |
| `comments` | text, default `""` | Comment |
| `itemPrice` | number, optional | Price per unit: Leave empty to use the product's own price in the deal's currency; 0 adds the line free. |

Upgrade @2 → @3: set `version` to 3 and replace the config — before, then after:
```json
{"quantity":1,"discount":0,"discountType":"percentage","tax":0,"productVariationId":0,"comments":"","connection":"<connection id>","dealId":"<deal id>","productId":1}
{"quantity":1,"discountType":"percentage","productVariationId":0,"comments":"","discount":0,"tax":0,"connection":"<connection id>","dealId":"<deal id>","productId":1}
```

## `pipedrive.createActivity@1` — Pipedrive — Create activity

Schedules a call, meeting or task on a Pipedrive record.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `subject` | text, required | Subject — its `{{ }}` must give a string |
| `type` | text, default `"call"` | Kind: Pipedrive's own key for the activity type — the List activity types node gives you every one your company has. |
| `dueDate` | text, default `""` | Due date — its `{{ }}` must give a string |
| `dueTime` | text, default `""` | Due time — its `{{ }}` must give a string |
| `duration` | text, default `""` | Duration — its `{{ }}` must give a string |
| `ownerId` | number, default `0` | Owner id: The List users node gives you the id. Leave at 0 for the token's own user. |
| `dealId` | number, default `0` | Deal id |
| `personId` | number, default `0` | Person id |
| `orgId` | number, default `0` | Organization id |
| `leadId` | text, default `""` | Lead id: A lead's id is a long identifier with dashes, not a number. — its `{{ }}` must give a string |
| `done` | true/false, default `false` | Already done |
| `busy` | true/false, default `false` | Blocks the calendar |
| `priority` | number, default `0` | Priority: Pipedrive's own priority number. Leave at 0 for none. |
| `publicDescription` | text, default `""` | Description guests see |
| `note` | text, default `""` | Private note |

Output: `activityId` string — Activity id · `activity` object — Activity.

```json
{"type":"pipedrive.createActivity","version":1,"label":"Create activity","config":{"type":"call","dueDate":"","dueTime":"","duration":"","ownerId":0,"dealId":0,"personId":0,"orgId":0,"leadId":"","done":false,"busy":false,"priority":0,"publicDescription":"","note":"","connection":"<connection id>","subject":"<subject>"}}
```

## `pipedrive.createDeal@1` — Pipedrive — Create deal

Opens a deal in Pipedrive, in the pipeline stage you name.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `title` | text, required | Title — its `{{ }}` must give a string |
| `pipelineId` | number, default `0` | Pipeline id: The List pipelines node gives you the id. Leave at 0 for the default pipeline. |
| `stageId` | number, default `0` | Stage id: The List pipelines node gives you every stage's id. |
| `personId` | number, default `0` | Person id |
| `orgId` | number, default `0` | Organization id |
| `ownerId` | number, default `0` | Owner id: The List users node gives you the id. Leave at 0 for the token's own user. |
| `value` | number, default `0` | Value |
| `currency` | text, default `""` | Currency: A three-letter code. Leave empty for the company's default. |
| `status` | "open" or "won" or "lost", default `"open"` | Status |
| `probability` | number, default `0` | Probability: 0 to 100. Only used when the pipeline has probability switched on. |
| `expectedCloseDate` | text, default `""` | Expected close date — its `{{ }}` must give a string |
| `visibleTo` | text, default `""` | Visible to: Pipedrive's own visibility number. Leave empty for the company's default. |
| `labelIds` | text, default `""` | Label ids: Comma-separated. Labels are ids in v2, not words. |
| `customFields` | object (any keys), default `{}` | Custom fields: Keys are the 40-character field code from the List field definitions node, not the label you see in Pipedrive. |

Output: `dealId` string — Deal id · `deal` object — Deal.

```json
{"type":"pipedrive.createDeal","version":1,"label":"Create deal","config":{"pipelineId":0,"stageId":0,"personId":0,"orgId":0,"ownerId":0,"value":0,"currency":"","status":"open","probability":0,"expectedCloseDate":"","visibleTo":"","labelIds":"","customFields":{},"connection":"<connection id>","title":"<title>"}}
```

## `pipedrive.createLead@1` — Pipedrive — Create lead

Adds a lead to the Pipedrive inbox before it becomes a deal.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `title` | text, required | Title — its `{{ }}` must give a string |
| `personId` | number, default `0` | Person id: A lead hangs on a person, an organization or both — fill in at least one of the two. |
| `organizationId` | number, default `0` | Organization id |
| `ownerId` | number, default `0` | Owner id: The List users node gives you the id. Leave at 0 for the token's own user. |
| `amount` | number, default `0` | Value |
| `currency` | text, default `""` | Currency: A three-letter code. Needed as soon as a value is filled in. |
| `expectedCloseDate` | text, default `""` | Expected close date — its `{{ }}` must give a string |
| `visibleTo` | text, default `""` | Visible to: Pipedrive's own visibility number. Leave empty for the company's default. |
| `labelIds` | text, default `""` | Label ids: Comma-separated. A lead label's id is a long identifier with dashes. |
| `channel` | number, default `0` | Channel: Pipedrive's own marketing-channel number. Leave at 0 for none. |
| `channelId` | text, default `""` | Channel id |

Output: `leadId` string — Lead id · `lead` object — Lead.

```json
{"type":"pipedrive.createLead","version":1,"label":"Create lead","config":{"personId":0,"organizationId":0,"ownerId":0,"amount":0,"currency":"","expectedCloseDate":"","visibleTo":"","labelIds":"","channel":0,"channelId":"","connection":"<connection id>","title":"<title>"}}
```

## `pipedrive.createNote@1` — Pipedrive — Create note

Writes a note on a Pipedrive deal, person, organization or lead.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `content` | text, required | Note: Pipedrive stores a note as HTML and sanitises it on its own side. |
| `attachTo` | "deal" or "person" or "organization" or "lead" or "project" or "task", default `"deal"` | Write it on |
| `attachToId` | text, required | Record id — its `{{ }}` must give a string |
| `userId` | number, default `0` | Write it as user id: Leave at 0 to write it as the token's own user. |
| `addTime` | text, default `""` | Time on the note — its `{{ }}` must give a string |
| `pinned` | true/false, default `false` | Pin it to the top: Keeps the note above the rest of that record's timeline. — used when `attachTo` is one of "deal", "person", "organization", "lead" |

Output: `noteId` string — Note id · `note` object — Note.

```json
{"type":"pipedrive.createNote","version":1,"label":"Create note","config":{"attachTo":"deal","userId":0,"addTime":"","pinned":false,"connection":"<connection id>","content":"<note>","attachToId":"<record id>"}}
```

## `pipedrive.createOrganization@1` — Pipedrive — Create organization

Adds a company to Pipedrive with its address and web links.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `name` | text, required | Name — its `{{ }}` must give a string |
| `ownerId` | number, default `0` | Owner id: The List users node gives you the id. Leave at 0 for the token's own user. |
| `address` | object (any keys), default `{}` | Address: Pipedrive's own address parts. Put the whole address in value and let Pipedrive split it, or fill in the parts yourself. |
| `website` | text, default `""` | Website — its `{{ }}` must give a string |
| `linkedin` | text, default `""` | LinkedIn — its `{{ }}` must give a string |
| `industry` | text, default `""` | Industry — its `{{ }}` must give a string |
| `annualRevenue` | number, default `0` | Annual revenue |
| `employeeCount` | number, default `0` | Employees |
| `visibleTo` | text, default `""` | Visible to: Pipedrive's own visibility number. Leave empty for the company's default. |
| `labelIds` | text, default `""` | Label ids: Comma-separated. Labels are ids in v2, not words. |
| `customFields` | object (any keys), default `{}` | Custom fields: Keys are the 40-character field code, not the label you see in Pipedrive. |

Output: `organizationId` string — Organization id · `organization` object — Organization.

```json
{"type":"pipedrive.createOrganization","version":1,"label":"Create organization","config":{"ownerId":0,"address":{},"website":"","linkedin":"","industry":"","annualRevenue":0,"employeeCount":0,"visibleTo":"","labelIds":"","customFields":{},"connection":"<connection id>","name":"<name>"}}
```

## `pipedrive.createPerson@1` — Pipedrive — Create person

Adds a person to Pipedrive with their e-mail and phone numbers.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `name` | text, required | Name — its `{{ }}` must give a string |
| `ownerId` | number, default `0` | Owner id: The List users node gives you the id. Leave at 0 for the token's own user. |
| `orgId` | number, default `0` | Organization id |
| `emails` | list of {value?: text, label?: "work" or "home" or "mobile" or "other", primary?: "true" or "false"}, default `[]` | E-mail addresses: One row per address. These rows are typed in, not taken from an earlier node — use the advanced E-mail addresses (whole list) field for that. |
| `phones` | list of {value?: text, label?: "work" or "home" or "mobile" or "other", primary?: "true" or "false"}, default `[]` | Phone numbers: One row per number. |
| `emailsJson` | any JSON, optional | E-mail addresses (whole list): A list of { value, label, primary } objects from an earlier node. When it is filled, the rows above are ignored. — its `{{ }}` must give an array |
| `phonesJson` | any JSON, optional | Phone numbers (whole list): Same shape as the e-mail list. When it is filled, the rows above are ignored. — its `{{ }}` must give an array |
| `visibleTo` | text, default `""` | Visible to: Pipedrive's own visibility number. Leave empty for the company's default. |
| `labelIds` | text, default `""` | Label ids: Comma-separated. Labels are ids in v2, not words. |
| `customFields` | object (any keys), default `{}` | Custom fields: Keys are the 40-character field code from the List field definitions node. A postal address, birthday or job title needs contact sync switched on for the company, and Pipedrive refuses them otherwise — reach those with an HTTP Request node. |

Output: `personId` string — Person id · `person` object — Person.

```json
{"type":"pipedrive.createPerson","version":1,"label":"Create person","config":{"ownerId":0,"orgId":0,"emails":[],"phones":[],"visibleTo":"","labelIds":"","customFields":{},"connection":"<connection id>","name":"<name>","emailsJson":"<e-mail addresses (whole list)>"}}
```

## `pipedrive.deleteRecord@1` — Pipedrive — Delete record

Deletes one Pipedrive record by its id.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `recordType` | "deals" or "persons" or "organizations" or "activities" or "products" or "leads" or "notes" or "files", default `"deals"` | Record |
| `recordId` | text, required | Record id — its `{{ }}` must give a string |

Output: `deleted` boolean — Deleted by this run · `alreadyGone` boolean — It was gone already · `recordId` string — Record id.

```json
{"type":"pipedrive.deleteRecord","version":1,"label":"Delete record","config":{"recordType":"deals","connection":"<connection id>","recordId":"<record id>"}}
```

## `pipedrive.downloadFile@1` — Pipedrive — Download file

Fetches the contents of a file stored in Pipedrive.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `fileId` | text, required | File id: The id the Upload file node answered, or one from a record's files. A download costs twenty tokens of the daily budget. — its `{{ }}` must give a string |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"pipedrive.downloadFile","version":1,"label":"Download file","config":{"connection":"<connection id>","fileId":"<file id>"}}
```

## `pipedrive.getRecord@1` — Pipedrive — Get record

Fetches one Pipedrive record by its id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `recordType` | "deals" or "persons" or "organizations" or "activities" or "products" or "leads" or "notes", default `"deals"` | Record |
| `recordId` | text, required | Record id — its `{{ }}` must give a string |
| `includeFields` | text, default `""` | Extra fields: Comma-separated. Fields Pipedrive leaves out unless you ask. |
| `customFields` | text, default `""` | Custom fields: Comma-separated field codes, at most 15. The List field definitions node turns a label into its code. |
| `includeOptionLabels` | true/false, default `false` | Spell out dropdown values |

Output: `record` object — Record · `recordId` string — Record id.

```json
{"type":"pipedrive.getRecord","version":1,"label":"Get record","config":{"recordType":"deals","includeFields":"","customFields":"","includeOptionLabels":false,"connection":"<connection id>","recordId":"<record id>"}}
```

## `pipedrive.listActivityTypes@1` — Pipedrive — List activity types

Lists the kinds of activity this Pipedrive company uses.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |

Output: `activityTypes` array — Activity types · `activityTypeCount` number — How many there are.

```json
{"type":"pipedrive.listActivityTypes","version":1,"label":"List activity types","config":{"connection":"<connection id>"}}
```

## `pipedrive.listFieldDefinitions@1` — Pipedrive — List field definitions

Turns a Pipedrive custom field's label into the code its API wants.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `noun` | "deal" or "person" or "organization" or "product" or "activity" or "project", default `"deal"` | Fields of |

Output: `fields` array — Field definitions · `fieldCount` number — How many there are.

```json
{"type":"pipedrive.listFieldDefinitions","version":1,"label":"List field definitions","config":{"noun":"deal","connection":"<connection id>"}}
```

## `pipedrive.listLeads@2` — Pipedrive — List leads

Lists the leads sitting in the Pipedrive inbox.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `ownerId` | text, default `""` | Owner id: The List users node gives you the id. — its `{{ }}` must give a string |
| `personId` | text, default `""` | Person id — its `{{ }}` must give a string |
| `organizationId` | text, default `""` | Organization id — its `{{ }}` must give a string |
| `filterId` | text, default `""` | Saved filter id: A filter you saved in Pipedrive. — its `{{ }}` must give a string |
| `archivedStatus` | "" or "archived" or "not_archived" or "all", default `""` | Archived |
| `sort` | text, default `""` | Sort by: A field name, with a leading minus for newest first. |
| `limit` | number, default `100` | Leads per call: 1 to 500. A lead list costs twenty tokens of the daily budget per call. |
| `start` | number, default `0` | Skip: How many leads to skip. This is offset paging, not a cursor. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Pipedrive's offset paging, and refuses past 5,000 leads rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `leads` array — Leads · `leadCount` number — Leads in this answer · `hasMore` boolean — More leads are waiting behind this page · `nextStart` number — Where the next page starts · `nextCursor` string — Cursor for the next page.

```json
{"type":"pipedrive.listLeads","version":2,"label":"List leads","config":{"ownerId":"","personId":"","organizationId":"","filterId":"","archivedStatus":"","sort":"","limit":100,"start":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `pipedrive.listLeads@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `ownerId` | text, default `""` | Owner id: The List users node gives you the id. — its `{{ }}` must give a string |
| `personId` | text, default `""` | Person id — its `{{ }}` must give a string |
| `organizationId` | text, default `""` | Organization id — its `{{ }}` must give a string |
| `filterId` | text, default `""` | Saved filter id: A filter you saved in Pipedrive. — its `{{ }}` must give a string |
| `archivedStatus` | "" or "archived" or "not_archived" or "all", default `""` | Archived |
| `sort` | text, default `""` | Sort by: A field name, with a leading minus for newest first. |
| `limit` | number, default `100` | Leads per call: 1 to 500. A lead list costs twenty tokens of the daily budget per call. |
| `start` | number, default `0` | Skip: How many leads to skip. This is offset paging, not a cursor. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Pipedrive's offset paging, and refuses past 5,000 leads rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"ownerId":"","personId":"","organizationId":"","filterId":"","archivedStatus":"","sort":"","limit":100,"start":0,"returnAll":false,"connection":"<connection id>"}
{"ownerId":"","personId":"","organizationId":"","filterId":"","archivedStatus":"","sort":"","limit":100,"start":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `pipedrive.listNotes@2` — Pipedrive — List notes

Lists the notes written on a Pipedrive record.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `dealId` | text, default `""` | Deal id — its `{{ }}` must give a string |
| `personId` | text, default `""` | Person id — its `{{ }}` must give a string |
| `orgId` | text, default `""` | Organization id — its `{{ }}` must give a string |
| `leadId` | text, default `""` | Lead id — its `{{ }}` must give a string |
| `userId` | text, default `""` | Written by user id — its `{{ }}` must give a string |
| `startDate` | text, default `""` | From date — its `{{ }}` must give a string |
| `endDate` | text, default `""` | Until date — its `{{ }}` must give a string |
| `sort` | text, default `""` | Sort by: A field name, with a leading minus for newest first. |
| `limit` | number, default `100` | Notes per call: 1 to 500. |
| `start` | number, default `0` | Skip: How many notes to skip. This is offset paging, not a cursor. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Pipedrive's offset paging, and refuses past 5,000 notes rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `notes` array — Notes · `noteCount` number — Notes in this answer · `hasMore` boolean — More notes are waiting behind this page · `nextStart` number — Where the next page starts · `nextCursor` string — Cursor for the next page.

```json
{"type":"pipedrive.listNotes","version":2,"label":"List notes","config":{"dealId":"","personId":"","orgId":"","leadId":"","userId":"","startDate":"","endDate":"","sort":"","limit":100,"start":0,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `pipedrive.listNotes@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `dealId` | text, default `""` | Deal id — its `{{ }}` must give a string |
| `personId` | text, default `""` | Person id — its `{{ }}` must give a string |
| `orgId` | text, default `""` | Organization id — its `{{ }}` must give a string |
| `leadId` | text, default `""` | Lead id — its `{{ }}` must give a string |
| `userId` | text, default `""` | Written by user id — its `{{ }}` must give a string |
| `startDate` | text, default `""` | From date — its `{{ }}` must give a string |
| `endDate` | text, default `""` | Until date — its `{{ }}` must give a string |
| `sort` | text, default `""` | Sort by: A field name, with a leading minus for newest first. |
| `limit` | number, default `100` | Notes per call: 1 to 500. |
| `start` | number, default `0` | Skip: How many notes to skip. This is offset paging, not a cursor. |
| `returnAll` | true/false, default `false` | Fetch every page: Walks Pipedrive's offset paging, and refuses past 5,000 notes rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"dealId":"","personId":"","orgId":"","leadId":"","userId":"","startDate":"","endDate":"","sort":"","limit":100,"start":0,"returnAll":false,"connection":"<connection id>"}
{"dealId":"","personId":"","orgId":"","leadId":"","userId":"","startDate":"","endDate":"","sort":"","limit":100,"start":0,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `pipedrive.listPipelines@1` — Pipedrive — List pipelines

Lists the Pipedrive pipelines and, if asked, their stages.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `includeStages` | true/false, default `true` | Include the stages: Reads each pipeline's stages as well — one extra call of five tokens per pipeline, and this node refuses past 50 of them. |

Output: `pipelines` array — Pipelines · `pipelineCount` number — How many there are.

```json
{"type":"pipedrive.listPipelines","version":1,"label":"List pipelines","config":{"includeStages":true,"connection":"<connection id>"}}
```

## `pipedrive.listRecords@1` — Pipedrive — List records

Lists Pipedrive deals, people, organizations, activities or products.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `recordType` | "deals" or "persons" or "organizations" or "activities" or "products", default `"deals"` | Records |
| `filterId` | text, default `""` | Saved filter id: A filter you saved in Pipedrive. When it is set, every other filter on this node is ignored — that is Pipedrive's own rule. — its `{{ }}` must give a string |
| `ownerId` | text, default `""` | Owner id: The List users node gives you the id. — a value from the vendor's own list |
| `personId` | text, default `""` | Person id — its `{{ }}` must give a string; used when `recordType` is one of "deals", "activities" |
| `orgId` | text, default `""` | Organization id — its `{{ }}` must give a string; used when `recordType` is one of "deals", "activities" |
| `pipelineId` | text, default `""` | Pipeline id — a value from the vendor's own list; used when `recordType` is "deals" |
| `stageId` | text, default `""` | Stage id — a value from the vendor's own list; used when `recordType` is "deals" |
| `status` | "" or "open" or "won" or "lost" or "deleted", default `""` | Status — used when `recordType` is "deals" |
| `done` | "" or "true" or "false", default `""` | Done — used when `recordType` is "activities" |
| `updatedSince` | text, default `""` | Changed since — its `{{ }}` must give a string |
| `updatedUntil` | text, default `""` | Changed before — its `{{ }}` must give a string |
| `sortBy` | "id" or "update_time" or "add_time", default `"id"` | Sort by |
| `sortDirection` | "asc" or "desc", default `"asc"` | Direction |
| `includeFields` | text, default `""` | Extra fields: Comma-separated. Fields Pipedrive leaves out of a list unless you ask. |
| `customFields` | text, default `""` | Custom fields: Comma-separated field codes, at most 15. The List field definitions node turns a label into its code. |
| `includeOptionLabels` | true/false, default `false` | Spell out dropdown values: Returns { id, label } for single and multi-select custom fields. |
| `includeLabels` | true/false, default `false` | Include labels |
| `limit` | number, default `100` | Records per call: 1 to 500. A list read costs about ten tokens of the daily budget however many records come back. |
| `cursor` | text, default `""` | Start after: A cursor from an earlier run of this node. Leave empty to start over. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Pipedrive's cursor, and refuses past 5,000 records rather than answering a short list. |

Output: `records` array — Records · `recordCount` number — Records in this answer · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"pipedrive.listRecords","version":1,"label":"List records","config":{"recordType":"deals","filterId":"","ownerId":"","personId":"","orgId":"","pipelineId":"","stageId":"","status":"","done":"","updatedSince":"","updatedUntil":"","sortBy":"id","sortDirection":"asc","includeFields":"","customFields":"","includeOptionLabels":false,"includeLabels":false,"limit":100,"cursor":"","returnAll":false,"connection":"<connection id>"}}
```

## `pipedrive.listUsers@1` — Pipedrive — List users

Lists the people in the Pipedrive company, with their ids.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |

Output: `users` array — Users · `userCount` number — How many there are.

```json
{"type":"pipedrive.listUsers","version":1,"label":"List users","config":{"connection":"<connection id>"}}
```

## `pipedrive.search@1` — Pipedrive — Search

Finds Pipedrive records by a search term across one or all item types.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `scope` | "all" or "deals" or "persons" or "organizations" or "leads" or "products", default `"persons"` | Search in |
| `term` | text, required | Search term: At least two characters, or one when Exact match is on. Search costs twenty to forty tokens of the daily budget and is capped at ten calls every two seconds on every plan. — its `{{ }}` must give a string |
| `fields` | text, default `""` | Fields to search: Comma-separated. People take custom_fields, email, name, notes, phone; deals take custom_fields, notes, title; organizations take address, custom_fields, name, notes. Leave empty for Pipedrive's own default. |
| `exactMatch` | true/false, default `false` | Exact match: Only whole-value matches, and then one character is enough. |
| `personId` | text, default `""` | Only for person id — its `{{ }}` must give a string; used when `scope` is "deals" |
| `organizationId` | text, default `""` | Only for organization id — its `{{ }}` must give a string; used when `scope` is one of "deals", "persons" |
| `status` | "" or "open" or "won" or "lost", default `""` | Only deals that are — used when `scope` is "deals" |
| `searchForRelatedItems` | true/false, default `false` | Include related records: Adds the deals and organizations a matching person is attached to. — used when `scope` is "all" |
| `includeFields` | text, default `""` | Extra fields: Comma-separated. Fields Pipedrive leaves out of a result unless you ask. |
| `limit` | number, default `100` | Results per call: 1 to 500. |
| `cursor` | text, default `""` | Start after: A cursor from an earlier run of this node. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Pipedrive's cursor, and refuses past 5,000 results rather than answering a short list. |

Output: `items` array — Matches · `itemCount` number — Matches in this answer · `hasMore` boolean — More matches are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"pipedrive.search","version":1,"label":"Search","config":{"scope":"persons","fields":"","exactMatch":false,"personId":"","organizationId":"","status":"","searchForRelatedItems":false,"includeFields":"","limit":100,"cursor":"","returnAll":false,"connection":"<connection id>","term":"<search term>"}}
```

## `pipedrive.updateActivity@1` — Pipedrive — Update activity

Reschedules a Pipedrive activity, or ticks it off.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `activityId` | text, required | Activity id — its `{{ }}` must give a string |
| `subject` | text, default `""` | Subject — its `{{ }}` must give a string |
| `type` | text, default `""` | Kind: Leave empty to keep the kind it has. |
| `dueDate` | text, default `""` | Due date — its `{{ }}` must give a string |
| `dueTime` | text, default `""` | Due time — its `{{ }}` must give a string |
| `duration` | text, default `""` | Duration — its `{{ }}` must give a string |
| `ownerId` | number, default `0` | Owner id |
| `dealId` | number, default `0` | Deal id |
| `personId` | number, default `0` | Person id |
| `orgId` | number, default `0` | Organization id |
| `done` | "" or "true" or "false", default `""` | Tick it off |
| `busy` | "" or "true" or "false", default `""` | Blocks the calendar |
| `priority` | number, default `0` | Priority |
| `publicDescription` | text, default `""` | Description guests see |
| `note` | text, default `""` | Private note |

Output: `activityId` string — Activity id · `activity` object — Activity.

```json
{"type":"pipedrive.updateActivity","version":1,"label":"Update activity","config":{"subject":"","type":"","dueDate":"","dueTime":"","duration":"","ownerId":0,"dealId":0,"personId":0,"orgId":0,"done":"","busy":"","priority":0,"publicDescription":"","note":"","connection":"<connection id>","activityId":"<activity id>"}}
```

## `pipedrive.updateDeal@2` — Pipedrive — Update deal

Moves a Pipedrive deal along, or changes what it says.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `dealId` | text, required | Deal id — its `{{ }}` must give a string |
| `title` | text, default `""` | Title — its `{{ }}` must give a string |
| `pipelineId` | number, default `0` | Pipeline id |
| `stageId` | number, default `0` | Move to stage id: The List pipelines node gives you every stage's id. Leave at 0 to leave it be. |
| `status` | "" or "open" or "won" or "lost", default `""` | Mark it |
| `lostReason` | text, default `""` | Reason it was lost — its `{{ }}` must give a string; used when `status` is "lost" |
| `personId` | number, default `0` | Person id |
| `orgId` | number, default `0` | Organization id |
| `ownerId` | number, default `0` | Owner id |
| `value` | number, optional | Value |
| `currency` | text, default `""` | Currency |
| `probability` | number, optional | Probability |
| `expectedCloseDate` | text, default `""` | Expected close date — its `{{ }}` must give a string |
| `labelIds` | text, default `""` | Label ids: Comma-separated. Labels are ids in v2, not words. |
| `customFields` | object (any keys), default `{}` | Custom fields: Keys are the 40-character field code, not the label you see in Pipedrive. |

Output: `dealId` string — Deal id · `deal` object — Deal.

```json
{"type":"pipedrive.updateDeal","version":2,"label":"Update deal","config":{"title":"","pipelineId":0,"stageId":0,"status":"","lostReason":"","personId":0,"orgId":0,"ownerId":0,"currency":"","expectedCloseDate":"","labelIds":"","customFields":{},"connection":"<connection id>","dealId":"<deal id>"}}
```

### Older version `pipedrive.updateDeal@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `dealId` | text, required | Deal id — its `{{ }}` must give a string |
| `title` | text, default `""` | Title — its `{{ }}` must give a string |
| `pipelineId` | number, default `0` | Pipeline id |
| `stageId` | number, default `0` | Move to stage id: The List pipelines node gives you every stage's id. Leave at 0 to leave it be. |
| `status` | "" or "open" or "won" or "lost", default `""` | Mark it |
| `lostReason` | text, default `""` | Reason it was lost — its `{{ }}` must give a string; used when `status` is "lost" |
| `personId` | number, default `0` | Person id |
| `orgId` | number, default `0` | Organization id |
| `ownerId` | number, default `0` | Owner id |
| `value` | number, default `0` | Value |
| `currency` | text, default `""` | Currency |
| `probability` | number, default `0` | Probability |
| `expectedCloseDate` | text, default `""` | Expected close date — its `{{ }}` must give a string |
| `labelIds` | text, default `""` | Label ids: Comma-separated. Labels are ids in v2, not words. |
| `customFields` | object (any keys), default `{}` | Custom fields: Keys are the 40-character field code, not the label you see in Pipedrive. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"title":"","pipelineId":0,"stageId":0,"status":"","lostReason":"","personId":0,"orgId":0,"ownerId":0,"value":0,"currency":"","probability":0,"expectedCloseDate":"","labelIds":"","customFields":{},"connection":"<connection id>","dealId":"<deal id>"}
{"title":"","pipelineId":0,"stageId":0,"status":"","lostReason":"","personId":0,"orgId":0,"ownerId":0,"currency":"","expectedCloseDate":"","labelIds":"","customFields":{},"value":0,"probability":0,"connection":"<connection id>","dealId":"<deal id>"}
```

## `pipedrive.updateLead@1` — Pipedrive — Update lead

Changes a lead in the Pipedrive inbox.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `leadId` | text, required | Lead id: A lead's id is a long identifier with dashes, not a number. — its `{{ }}` must give a string |
| `title` | text, default `""` | Title — its `{{ }}` must give a string |
| `personId` | number, default `0` | Person id |
| `organizationId` | number, default `0` | Organization id |
| `ownerId` | number, default `0` | Owner id |
| `amount` | number, default `0` | Value |
| `currency` | text, default `""` | Currency: A three-letter code. Needed as soon as a value is filled in. |
| `expectedCloseDate` | text, default `""` | Expected close date — its `{{ }}` must give a string |
| `visibleTo` | text, default `""` | Visible to: Pipedrive's own visibility number. Leave empty to keep what it has. |
| `labelIds` | text, default `""` | Label ids: Comma-separated. A lead label's id is a long identifier with dashes. |

Output: `leadId` string — Lead id · `lead` object — Lead.

```json
{"type":"pipedrive.updateLead","version":1,"label":"Update lead","config":{"title":"","personId":0,"organizationId":0,"ownerId":0,"amount":0,"currency":"","expectedCloseDate":"","visibleTo":"","labelIds":"","connection":"<connection id>","leadId":"<lead id>"}}
```

## `pipedrive.updateOrganization@2` — Pipedrive — Update organization

Changes a company in Pipedrive — its address, web links or owner.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `organizationId` | text, required | Organization id — its `{{ }}` must give a string |
| `name` | text, default `""` | Name — its `{{ }}` must give a string |
| `ownerId` | number, default `0` | Owner id: The List users node gives you the id. Leave at 0 for the token's own user. |
| `address` | object (any keys), default `{}` | Address: Pipedrive's own address parts. Put the whole address in value and let Pipedrive split it, or fill in the parts yourself. |
| `website` | text, default `""` | Website — its `{{ }}` must give a string |
| `linkedin` | text, default `""` | LinkedIn — its `{{ }}` must give a string |
| `industry` | text, default `""` | Industry — its `{{ }}` must give a string |
| `annualRevenue` | number, optional | Annual revenue |
| `employeeCount` | number, optional | Employees |
| `visibleTo` | text, default `""` | Visible to: Pipedrive's own visibility number. Leave empty for the company's default. |
| `labelIds` | text, default `""` | Label ids: Comma-separated. Labels are ids in v2, not words. |
| `customFields` | object (any keys), default `{}` | Custom fields: Keys are the 40-character field code, not the label you see in Pipedrive. |

Output: `organizationId` string — Organization id · `organization` object — Organization.

```json
{"type":"pipedrive.updateOrganization","version":2,"label":"Update organization","config":{"name":"","ownerId":0,"address":{},"website":"","linkedin":"","industry":"","visibleTo":"","labelIds":"","customFields":{},"connection":"<connection id>","organizationId":"<organization id>"}}
```

### Older version `pipedrive.updateOrganization@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `organizationId` | text, required | Organization id — its `{{ }}` must give a string |
| `name` | text, default `""` | Name — its `{{ }}` must give a string |
| `ownerId` | number, default `0` | Owner id: The List users node gives you the id. Leave at 0 for the token's own user. |
| `address` | object (any keys), default `{}` | Address: Pipedrive's own address parts. Put the whole address in value and let Pipedrive split it, or fill in the parts yourself. |
| `website` | text, default `""` | Website — its `{{ }}` must give a string |
| `linkedin` | text, default `""` | LinkedIn — its `{{ }}` must give a string |
| `industry` | text, default `""` | Industry — its `{{ }}` must give a string |
| `annualRevenue` | number, default `0` | Annual revenue |
| `employeeCount` | number, default `0` | Employees |
| `visibleTo` | text, default `""` | Visible to: Pipedrive's own visibility number. Leave empty for the company's default. |
| `labelIds` | text, default `""` | Label ids: Comma-separated. Labels are ids in v2, not words. |
| `customFields` | object (any keys), default `{}` | Custom fields: Keys are the 40-character field code, not the label you see in Pipedrive. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"name":"","ownerId":0,"address":{},"website":"","linkedin":"","industry":"","annualRevenue":0,"employeeCount":0,"visibleTo":"","labelIds":"","customFields":{},"connection":"<connection id>","organizationId":"<organization id>"}
{"name":"","ownerId":0,"address":{},"website":"","linkedin":"","industry":"","visibleTo":"","labelIds":"","customFields":{},"annualRevenue":0,"employeeCount":0,"connection":"<connection id>","organizationId":"<organization id>"}
```

## `pipedrive.updatePerson@1` — Pipedrive — Update person

Changes a person in Pipedrive — their name, owner, contact details or labels.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `personId` | text, required | Person id — its `{{ }}` must give a string |
| `name` | text, default `""` | Name — its `{{ }}` must give a string |
| `ownerId` | number, default `0` | Owner id: The List users node gives you the id. Leave at 0 for the token's own user. |
| `orgId` | number, default `0` | Organization id |
| `emails` | list of {value?: text, label?: "work" or "home" or "mobile" or "other", primary?: "true" or "false"}, default `[]` | E-mail addresses: One row per address. These rows are typed in, not taken from an earlier node — use the advanced E-mail addresses (whole list) field for that. |
| `phones` | list of {value?: text, label?: "work" or "home" or "mobile" or "other", primary?: "true" or "false"}, default `[]` | Phone numbers: One row per number. |
| `emailsJson` | any JSON, optional | E-mail addresses (whole list): A list of { value, label, primary } objects from an earlier node. When it is filled, the rows above are ignored. — its `{{ }}` must give an array |
| `phonesJson` | any JSON, optional | Phone numbers (whole list): Same shape as the e-mail list. When it is filled, the rows above are ignored. — its `{{ }}` must give an array |
| `visibleTo` | text, default `""` | Visible to: Pipedrive's own visibility number. Leave empty for the company's default. |
| `labelIds` | text, default `""` | Label ids: Comma-separated. Labels are ids in v2, not words. |
| `customFields` | object (any keys), default `{}` | Custom fields: Keys are the 40-character field code from the List field definitions node. A postal address, birthday or job title needs contact sync switched on for the company, and Pipedrive refuses them otherwise — reach those with an HTTP Request node. |

Output: `personId` string — Person id · `person` object — Person.

```json
{"type":"pipedrive.updatePerson","version":1,"label":"Update person","config":{"name":"","ownerId":0,"orgId":0,"emails":[],"phones":[],"visibleTo":"","labelIds":"","customFields":{},"connection":"<connection id>","personId":"<person id>","emailsJson":"<e-mail addresses (whole list)>"}}
```

## `pipedrive.uploadFile@1` — Pipedrive — Upload file

Attaches a file to a Pipedrive deal, person or organization.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `pipedrive`) |
| `name` | text, required | File name: The file's name, extension included. Pick the file, then paste its base64 below. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("name").base64 }}. — its `{{ }}` must give a string |
| `attachTo` | "deal" or "person" or "organization" or "product" or "activity" or "lead" or "project", default `"deal"` | Attach it to |
| `attachToId` | text, required | Record id — its `{{ }}` must give a string |

Output: `file` object — File · `fileId` string — File id.

```json
{"type":"pipedrive.uploadFile","version":1,"label":"Upload file","config":{"attachTo":"deal","connection":"<connection id>","name":"<file name>","contentBase64":"<file contents>","attachToId":"<record id>"}}
```


---

## File: references/nodes/postgres.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# PostgreSQL nodes (`postgres.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `postgres.query@1` — PostgreSQL — Run SQL query

Reads or changes rows in a PostgreSQL database with SQL you write.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `postgres`) |
| `operation` | "select" or "execute", default `"select"` | Operation |
| `query` | text, required | Query: Write values from earlier steps as $1, $2 … and list them under Parameters. {{ }} is not filled in here. — plain text, never `{{ }}` |
| `params` | list of any JSON, default `[]` | Parameters: One value per placeholder, in order. Wrap JSON for a json column with $1::jsonb. |
| `maxRows` | number, default `1000` | Max rows: Default 1,000 — up to the workspace limit. |
| `ifMoreRows` | "fail" or "first", default `"fail"` | If more rows — used when `operation` is "select" |

Output: `rows` array — Rows · `rowCount` number — Rows returned · `columns` array — Columns — name and type · `truncated` boolean — More rows than Max rows — only the first came back · `affectedRows` number — Rows the statement changed.

```json
{"type":"postgres.query","version":1,"label":"Run SQL query","config":{"operation":"select","params":[],"maxRows":1000,"ifMoreRows":"fail","connection":"<connection id>","query":"<query>"}}
```


---

## File: references/nodes/recruitee.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Recruitee nodes (`recruitee.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `recruitee.addNote@1` — Recruitee — Add note

Writes an internal note on a Recruitee candidate.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `candidateId` | text, required | Candidate id: The candidate's Recruitee id — a number. |
| `body` | text, required | Note |
| `offerId` | text, default `""` | Job id: Attach the note to one job instead of the candidate's profile. |

Output: `note` object — Note · `note.id` number — Id · `note.created_at` string — Written at.

```json
{"type":"recruitee.addNote","version":1,"label":"Add note","config":{"offerId":"","connection":"<connection id>","candidateId":"<candidate id>","body":"<note>"}}
```

## `recruitee.createCandidate@1` — Recruitee — Create candidate

Adds a candidate to Recruitee, optionally straight onto a job.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `name` | text, required | Name |
| `emails` | text, default `""` | E-mail addresses: Up to 10 addresses. |
| `phones` | text, default `""` | Phone numbers |
| `offerIds` | text, default `""` | Job ids: Recruitee job ids to place the candidate on, comma-separated. Leave empty to add them to the talent pool only. |
| `remoteCvUrl` | text, default `""` | CV address: A public https link to the CV. Recruitee downloads it. |
| `coverLetter` | text, default `""` | Cover letter |
| `sources` | text, default `""` | Sources: Comma-separated, free text. |
| `links` | text, default `""` | Links: Comma-separated URLs. |

Output: `candidate` object — Candidate · `candidate.id` number — Id · `candidate.name` string — Name · `candidate.placements` array — The jobs this candidate is on · `references` array — Referenced jobs, stages and admins.

```json
{"type":"recruitee.createCandidate","version":1,"label":"Create candidate","config":{"emails":"","phones":"","offerIds":"","remoteCvUrl":"","coverLetter":"","sources":"","links":"","connection":"<connection id>","name":"<name>"}}
```

## `recruitee.findCandidates@2` — Recruitee — Find candidates

Finds candidates in Recruitee by name, email, tag or CV content.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `query` | text, default `""` | Search: Free text — Recruitee searches names, e-mail addresses, phone numbers, tags, sources, cover letters and CV contents. |
| `filters` | any JSON, optional | Filters: Recruitee's own filters_json value. Time filters take Unix timestamps. |
| `sortBy` | "created_at_desc" or "created_at_asc" or "updated_at_desc" or "updated_at_asc", default `"created_at_desc"` | Sort by |
| `limit` | number, default `60` | Candidates per call: 1 to 1,000. Recruitee allows up to 10,000 but one node output cannot carry that. With Fetch every page on, this is the page size. |
| `page` | number, default `1` | Page: Which page to read when Fetch every page is off. |
| `returnAll` | true/false, default `false` | Fetch every page: Pages until Recruitee runs out, and refuses past 10,000 candidates rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `candidates` array — Candidates · `candidateCount` number — Candidates in this answer · `total` number — How many Recruitee found in total · `hasMore` boolean — More candidates are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"recruitee.findCandidates","version":2,"label":"Find candidates","config":{"query":"","sortBy":"created_at_desc","limit":60,"page":1,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `recruitee.findCandidates@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `query` | text, default `""` | Search: Free text — Recruitee searches names, e-mail addresses, phone numbers, tags, sources, cover letters and CV contents. |
| `filters` | any JSON, optional | Filters: Recruitee's own filters_json value. Time filters take Unix timestamps. |
| `sortBy` | "created_at_desc" or "created_at_asc" or "updated_at_desc" or "updated_at_asc", default `"created_at_desc"` | Sort by |
| `limit` | number, default `60` | Candidates per call: 1 to 1,000. Recruitee allows up to 10,000 but one node output cannot carry that. With Fetch every page on, this is the page size. |
| `page` | number, default `1` | Page: Which page to read when Fetch every page is off. |
| `returnAll` | true/false, default `false` | Fetch every page: Pages until Recruitee runs out, and refuses past 10,000 candidates rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"query":"","sortBy":"created_at_desc","limit":60,"page":1,"returnAll":false,"connection":"<connection id>"}
{"query":"","sortBy":"created_at_desc","limit":60,"page":1,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `recruitee.getCandidate@1` — Recruitee — Get candidate

Fetches one Recruitee candidate with their jobs, tags and profile fields.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `candidateId` | text, required | Candidate id: The candidate's Recruitee id — a number. The Find candidates node hands you candidates[].id. |

Output: `candidate` object — Candidate · `candidate.id` number — Id · `candidate.name` string — Name · `candidate.emails` array — E-mail addresses · `candidate.phones` array — Phone numbers · `candidate.tags` array — Tags · `candidate.placements` array — The jobs this candidate is on · `candidate.cv_url` string — CV address · `references` array — Referenced jobs, stages and admins.

```json
{"type":"recruitee.getCandidate","version":1,"label":"Get candidate","config":{"connection":"<connection id>","candidateId":"<candidate id>"}}
```

## `recruitee.getJob@1` — Recruitee — Get job

Fetches one Recruitee job or talent pool by id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `offerId` | text, required | Job id: The job's Recruitee id — a number. List jobs hands you offers[].id. |

Output: `offer` object — Job · `offer.id` number — Id · `offer.title` string — Title · `offer.status` string — Status · `offer.kind` string — Job or talent pool · `offer.pipeline_template` object — Pipeline.

```json
{"type":"recruitee.getJob","version":1,"label":"Get job","config":{"connection":"<connection id>","offerId":"<job id>"}}
```

## `recruitee.listJobs@1` — Recruitee — List jobs

Lists the jobs and talent pools in Recruitee.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `scope` | "active" or "archived" or "all", default `"active"` | Jobs: Recruitee calls this endpoint legacy and gives it no paging — this is one answer. |
| `viewMode` | "brief" or "default", default `"brief"` | Detail |

Output: `offers` array — Jobs and talent pools · `offerCount` number — How many this answer holds.

```json
{"type":"recruitee.listJobs","version":1,"label":"List jobs","config":{"scope":"active","viewMode":"brief","connection":"<connection id>"}}
```

## `recruitee.moveCandidate@1` — Recruitee — Move candidate to stage

Moves a Recruitee candidate to another stage of a job's pipeline.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `placementId` | text, required | Placement id: Recruitee's word for candidate-on-a-job. Get it from Get candidate → candidate.placements[].id, or Find candidates → candidates[].placements[].id. |
| `stageId` | text, required | Stage id: The pipeline stage to move the candidate to. |

Output: `placement` object — Placement · `placement.id` number — Id · `placement.stage_id` number — Stage id.

```json
{"type":"recruitee.moveCandidate","version":1,"label":"Move candidate to stage","config":{"connection":"<connection id>","placementId":"<placement id>","stageId":"<stage id>"}}
```

## `recruitee.setCandidateField@1` — Recruitee — Set profile field

Sets a profile field on a Recruitee candidate.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `candidateId` | text, required | Candidate id: The candidate's Recruitee id — a number. |
| `kind` | "single_line" or "multi_line" or "boolean" or "address" or "gender" or "number" or "nationality" or "language_skill" or "skills" or "date_of_birth" or "date" or "experience" or "education" or "salary", default `"single_line"` | Field type |
| `fieldName` | text, default `""` | Field name |
| `values` | any JSON, optional | Value: What the field holds. A one-line text field takes [{ "text": "…" }]; a yes/no takes [{ "flag": true }]; a number [{ "number": 40 }]. |

Output: `field` object — Field · `field.id` number — Id · `field.kind` string — Field type · `field.name` string — Field name · `field.values` array — Value.

```json
{"type":"recruitee.setCandidateField","version":1,"label":"Set profile field","config":{"kind":"single_line","fieldName":"","connection":"<connection id>","candidateId":"<candidate id>"}}
```

## `recruitee.updateCandidate@1` — Recruitee — Update candidate

Changes a Recruitee candidate's name, e-mail addresses, phones or links.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `candidateId` | text, required | Candidate id: The candidate's Recruitee id — a number. |
| `name` | text, optional | Name |
| `emails` | text, optional | E-mail addresses |
| `phones` | text, optional | Phone numbers |
| `socialLinks` | text, optional | Social links: Comma-separated URLs. |
| `links` | text, optional | Links: Comma-separated URLs. |
| `coverLetter` | text, optional | Cover letter |
| `remoteCvUrl` | text, optional | CV address |

Output: `candidate` object — Candidate · `candidate.id` number — Id · `candidate.updated_at` string — Changed at.

```json
{"type":"recruitee.updateCandidate","version":1,"label":"Update candidate","config":{"connection":"<connection id>","candidateId":"<candidate id>"}}
```

## `recruitee.uploadAttachment@1` — Recruitee — Attach file

Attaches a file to a Recruitee candidate or job.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruitee`) |
| `target` | "candidate" or "offer", default `"candidate"` | Attach to |
| `targetId` | text, required | Id: The candidate's or job's Recruitee id — a number. |
| `source` | "file" or "url", default `"file"` | File source |
| `fileName` | text, default `""` | File: Pick the file to attach. Then paste its base64 below. — a file name, listed in `requires.files`; used when `source` is "file" |
| `contentBase64` | text, default `""` | File contents: Base64 of the file — {{ file("name").base64 }} for the file picked above. Multipart uploads over the "Use fixed IP address" lane are unverified; leave that toggle off for this node. — its `{{ }}` must give a string; used when `source` is "file" |
| `remoteFileUrl` | text, default `""` | File address — used when `source` is "url" |

Output: `attachment` object — Attachment · `attachment.id` number — Id · `attachment.filename` string — File name · `attachment.file_url` string — File address · `attachment.file_size` number — Size in bytes.

```json
{"type":"recruitee.uploadAttachment","version":1,"label":"Attach file","config":{"target":"candidate","source":"file","fileName":"","contentBase64":"","remoteFileUrl":"","connection":"<connection id>","targetId":"<id>"}}
```


---

## File: references/nodes/recruition.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Recruition nodes (`recruition.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `recruition.addFile@1` — Recruition — Add file

Adds a file to a Recruition candidate or vacancy.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `fields` | object (any keys), default `{}` | Fields: One row per column, typed. An empty value is sent as it is — Recruition's own rules decide. |
| `data` | any JSON, optional | Whole body (JSON): The whole request body as JSON, for a shape the rows above cannot hold. When this is filled, the rows are ignored. |

Output: `record` any — Recruition answer.

```json
{"type":"recruition.addFile","version":1,"label":"Add file","config":{"fields":{},"connection":"<connection id>"}}
```

## `recruition.addMemo@1` — Recruition — Add memo

Writes a memo on a Recruition record.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `fields` | object (any keys), default `{}` | Fields: One row per column, typed. An empty value is sent as it is — Recruition's own rules decide. |
| `data` | any JSON, optional | Whole body (JSON): The whole request body as JSON, for a shape the rows above cannot hold. When this is filled, the rows are ignored. |
| `recordType` | text, required | On record type: The kind of record the memo hangs on — Recruition's own type name. |
| `recordId` | text, required | On record id: The id of the record the memo hangs on. |
| `memo` | text, required | Memo |

Output: `record` any — Recruition answer.

```json
{"type":"recruition.addMemo","version":1,"label":"Add memo","config":{"fields":{},"connection":"<connection id>","recordType":"<on record type>","recordId":"<on record id>","memo":"<memo>"}}
```

## `recruition.createApplication@1` — Recruition — Add application

Puts a candidate on a Recruition vacancy as a job application.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `fields` | object (any keys), default `{}` | Fields: One row per column, typed. An empty value is sent as it is — Recruition's own rules decide. |
| `data` | any JSON, optional | Whole body (JSON): The whole request body as JSON, for a shape the rows above cannot hold. When this is filled, the rows are ignored. |
| `candidateId` | text, required | Candidate id: The candidate to put on the vacancy. |
| `vacancyId` | text, required | Vacancy id: The vacancy the candidate is put on. |

Output: `record` any — Recruition answer.

```json
{"type":"recruition.createApplication","version":1,"label":"Add application","config":{"fields":{},"connection":"<connection id>","candidateId":"<candidate id>","vacancyId":"<vacancy id>"}}
```

## `recruition.createCandidate@1` — Recruition — Create candidate

Creates a candidate in Recruition.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `fields` | object (any keys), default `{}` | Fields: One row per column, typed. An empty value is sent as it is — Recruition's own rules decide. |
| `data` | any JSON, optional | Whole body (JSON): The whole request body as JSON, for a shape the rows above cannot hold. When this is filled, the rows are ignored. |

Output: `record` any — Recruition answer.

```json
{"type":"recruition.createCandidate","version":1,"label":"Create candidate","config":{"fields":{},"connection":"<connection id>"}}
```

## `recruition.createTask@1` — Recruition — Create task

Creates a task in Recruition for a user.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `fields` | object (any keys), default `{}` | Fields: One row per column, typed. An empty value is sent as it is — Recruition's own rules decide. |
| `data` | any JSON, optional | Whole body (JSON): The whole request body as JSON, for a shape the rows above cannot hold. When this is filled, the rows are ignored. |

Output: `record` any — Recruition answer.

```json
{"type":"recruition.createTask","version":1,"label":"Create task","config":{"fields":{},"connection":"<connection id>"}}
```

## `recruition.createVacancy@1` — Recruition — Create vacancy

Creates a vacancy in Recruition.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `fields` | object (any keys), default `{}` | Fields: One row per column, typed. An empty value is sent as it is — Recruition's own rules decide. |
| `data` | any JSON, optional | Whole body (JSON): The whole request body as JSON, for a shape the rows above cannot hold. When this is filled, the rows are ignored. |

Output: `record` any — Recruition answer.

```json
{"type":"recruition.createVacancy","version":1,"label":"Create vacancy","config":{"fields":{},"connection":"<connection id>"}}
```

## `recruition.deleteCandidate@1` — Recruition — Delete candidate

Deletes a Recruition candidate for good.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `id` | text, required | ID: The candidate's id. |

Output: `deleted` boolean — Deleted.

```json
{"type":"recruition.deleteCandidate","version":1,"label":"Delete candidate","config":{"connection":"<connection id>","id":"<id>"}}
```

## `recruition.findApplications@2` — Recruition — Find applications

Finds Recruition job applications by vacancy, candidate or state.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `filters` | list of {field?: text, value?: text}, default `[]` | Filters: One row per filter. Custom fields use Recruition's own spelling: custom_field_data->name. This resource's own fields: id, vacancyid, candidateid, state, createdon, canceled, created_at. |
| `sorts` | list of {field?: text, direction?: "asc" or "desc"}, default `[]` | Sort by |
| `query` | object of text, default `{}` | Extra query parameters: Anything else the endpoint takes — Recruition's own limit, or a parameter its swagger names. Values here may carry expressions. |
| `perPage` | number, default `25` | Records per page: 1 to 100. |
| `page` | number, default `1` | First page |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on page until Recruition's next-page link is gone, and refuses past Stop after rather than answering a short list. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 100. Reaching it fails the node rather than truncating. — used when `returnAll` is true |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Records · `itemCount` number — Records in this answer · `total` number — How many Recruition counted · `page` number — Page · `lastPage` number — Last page · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"recruition.findApplications","version":2,"label":"Find applications","config":{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}}
```

### Older version `recruition.findApplications@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `filters` | list of {field?: text, value?: text}, default `[]` | Filters: One row per filter. Custom fields use Recruition's own spelling: custom_field_data->name. This resource's own fields: id, vacancyid, candidateid, state, createdon, canceled, created_at. |
| `sorts` | list of {field?: text, direction?: "asc" or "desc"}, default `[]` | Sort by |
| `query` | object of text, default `{}` | Extra query parameters: Anything else the endpoint takes — Recruition's own limit, or a parameter its swagger names. Values here may carry expressions. |
| `perPage` | number, default `25` | Records per page: 1 to 100. |
| `page` | number, default `1` | First page |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on page until Recruition's next-page link is gone, and refuses past Stop after rather than answering a short list. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 100. Reaching it fails the node rather than truncating. — used when `returnAll` is true |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"connection":"<connection id>"}
{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}
```

## `recruition.findCandidates@2` — Recruition — Find candidates

Finds candidates in Recruition by any of its own fields.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `filters` | list of {field?: text, value?: text}, default `[]` | Filters: One row per filter. Custom fields use Recruition's own spelling: custom_field_data->name. This resource's own fields: id, initials, title, firstname, middlename, lastname, birthday, geboorteplaats, gender, email, phone, mobile, address, house_number, street, zipcode, district, city, province, country, maxkm, profile, education, educationlevel, educationhigh, workexperience, experience, hobbys, interview_report, other, description, searchfield, linkedinprofileurl, availability, weekavailable, hours, contract_start, contract_end, employee, gdpr_from, gdpr_till, anonymized, anonymous, source, origin, branche, functie, sector, office_id, companyid, priority_id, intermediary_id, language_code, shared, new, onwebsite, vacancymail, birthdaymail, archive, createdon, salary, custom_field_data->name. |
| `sorts` | list of {field?: text, direction?: "asc" or "desc"}, default `[]` | Sort by |
| `query` | object of text, default `{}` | Extra query parameters: Anything else the endpoint takes — Recruition's own limit, or a parameter its swagger names. Values here may carry expressions. |
| `perPage` | number, default `25` | Records per page: 1 to 100. |
| `page` | number, default `1` | First page |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on page until Recruition's next-page link is gone, and refuses past Stop after rather than answering a short list. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 100. Reaching it fails the node rather than truncating. — used when `returnAll` is true |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Records · `itemCount` number — Records in this answer · `total` number — How many Recruition counted · `page` number — Page · `lastPage` number — Last page · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"recruition.findCandidates","version":2,"label":"Find candidates","config":{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}}
```

### Older version `recruition.findCandidates@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `filters` | list of {field?: text, value?: text}, default `[]` | Filters: One row per filter. Custom fields use Recruition's own spelling: custom_field_data->name. This resource's own fields: id, initials, title, firstname, middlename, lastname, birthday, geboorteplaats, gender, email, phone, mobile, address, house_number, street, zipcode, district, city, province, country, maxkm, profile, education, educationlevel, educationhigh, workexperience, experience, hobbys, interview_report, other, description, searchfield, linkedinprofileurl, availability, weekavailable, hours, contract_start, contract_end, employee, gdpr_from, gdpr_till, anonymized, anonymous, source, origin, branche, functie, sector, office_id, companyid, priority_id, intermediary_id, language_code, shared, new, onwebsite, vacancymail, birthdaymail, archive, createdon, salary, custom_field_data->name. |
| `sorts` | list of {field?: text, direction?: "asc" or "desc"}, default `[]` | Sort by |
| `query` | object of text, default `{}` | Extra query parameters: Anything else the endpoint takes — Recruition's own limit, or a parameter its swagger names. Values here may carry expressions. |
| `perPage` | number, default `25` | Records per page: 1 to 100. |
| `page` | number, default `1` | First page |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on page until Recruition's next-page link is gone, and refuses past Stop after rather than answering a short list. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 100. Reaching it fails the node rather than truncating. — used when `returnAll` is true |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"connection":"<connection id>"}
{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}
```

## `recruition.findFiles@2` — Recruition — Find files

Finds files attached to Recruition records.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `filters` | list of {field?: text, value?: text}, default `[]` | Filters: One row per filter. Custom fields use Recruition's own spelling: custom_field_data->name. This resource's own fields: id, name, candidateid, type, omschrijving, description, status_id, valid_from, valid_to. |
| `sorts` | list of {field?: text, direction?: "asc" or "desc"}, default `[]` | Sort by |
| `query` | object of text, default `{}` | Extra query parameters: Anything else the endpoint takes — Recruition's own limit, or a parameter its swagger names. Values here may carry expressions. |
| `perPage` | number, default `25` | Records per page: 1 to 100. |
| `page` | number, default `1` | First page |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on page until Recruition's next-page link is gone, and refuses past Stop after rather than answering a short list. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 100. Reaching it fails the node rather than truncating. — used when `returnAll` is true |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Records · `itemCount` number — Records in this answer · `total` number — How many Recruition counted · `page` number — Page · `lastPage` number — Last page · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"recruition.findFiles","version":2,"label":"Find files","config":{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}}
```

### Older version `recruition.findFiles@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `filters` | list of {field?: text, value?: text}, default `[]` | Filters: One row per filter. Custom fields use Recruition's own spelling: custom_field_data->name. This resource's own fields: id, name, candidateid, type, omschrijving, description, status_id, valid_from, valid_to. |
| `sorts` | list of {field?: text, direction?: "asc" or "desc"}, default `[]` | Sort by |
| `query` | object of text, default `{}` | Extra query parameters: Anything else the endpoint takes — Recruition's own limit, or a parameter its swagger names. Values here may carry expressions. |
| `perPage` | number, default `25` | Records per page: 1 to 100. |
| `page` | number, default `1` | First page |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on page until Recruition's next-page link is gone, and refuses past Stop after rather than answering a short list. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 100. Reaching it fails the node rather than truncating. — used when `returnAll` is true |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"connection":"<connection id>"}
{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}
```

## `recruition.findVacancies@2` — Recruition — Find vacancies

Finds vacancies in Recruition by company, status, location or function.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `filters` | list of {field?: text, value?: text}, default `[]` | Filters: One row per filter. Custom fields use Recruition's own spelling: custom_field_data->name. This resource's own fields: id, employee, company, branche, sector, function, profile_id, office_id, name, status_id, showonwebsite, description, fte_hours, fte_amount, hours, contract, country_id, area, street, house_number, postcode, lat, lng, anonymous, requirements, salaryindication, salarymax, min_salary, max_salary, salary_period, salary_range_id, education, date, specialties, internalnotes, archive, propaid, custom_field_data->name. |
| `sorts` | list of {field?: text, direction?: "asc" or "desc"}, default `[]` | Sort by |
| `query` | object of text, default `{}` | Extra query parameters: Anything else the endpoint takes — Recruition's own limit, or a parameter its swagger names. Values here may carry expressions. |
| `perPage` | number, default `25` | Records per page: 1 to 100. |
| `page` | number, default `1` | First page |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on page until Recruition's next-page link is gone, and refuses past Stop after rather than answering a short list. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 100. Reaching it fails the node rather than truncating. — used when `returnAll` is true |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Records · `itemCount` number — Records in this answer · `total` number — How many Recruition counted · `page` number — Page · `lastPage` number — Last page · `hasMore` boolean — More records are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"recruition.findVacancies","version":2,"label":"Find vacancies","config":{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}}
```

### Older version `recruition.findVacancies@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `filters` | list of {field?: text, value?: text}, default `[]` | Filters: One row per filter. Custom fields use Recruition's own spelling: custom_field_data->name. This resource's own fields: id, employee, company, branche, sector, function, profile_id, office_id, name, status_id, showonwebsite, description, fte_hours, fte_amount, hours, contract, country_id, area, street, house_number, postcode, lat, lng, anonymous, requirements, salaryindication, salarymax, min_salary, max_salary, salary_period, salary_range_id, education, date, specialties, internalnotes, archive, propaid, custom_field_data->name. |
| `sorts` | list of {field?: text, direction?: "asc" or "desc"}, default `[]` | Sort by |
| `query` | object of text, default `{}` | Extra query parameters: Anything else the endpoint takes — Recruition's own limit, or a parameter its swagger names. Values here may carry expressions. |
| `perPage` | number, default `25` | Records per page: 1 to 100. |
| `page` | number, default `1` | First page |
| `returnAll` | true/false, default `false` | Fetch every page: Pages on page until Recruition's next-page link is gone, and refuses past Stop after rather than answering a short list. |
| `maxPages` | number, default `10` | Stop after: Pages — at most 100. Reaching it fails the node rather than truncating. — used when `returnAll` is true |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"connection":"<connection id>"}
{"filters":[],"sorts":[],"query":{},"perPage":25,"page":1,"returnAll":false,"maxPages":10,"cursor":"","connection":"<connection id>"}
```

## `recruition.getApplication@1` — Recruition — Get application

Fetches one Recruition job application by id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `id` | text, required | ID: The application's id — a Find applications node hands you items[].id. |

Output: `record` any — Record.

```json
{"type":"recruition.getApplication","version":1,"label":"Get application","config":{"connection":"<connection id>","id":"<id>"}}
```

## `recruition.getCandidate@1` — Recruition — Get candidate

Fetches one Recruition candidate by id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `id` | text, required | ID: The candidate's id — a Find candidates node hands you items[].id. |

Output: `record` any — Record.

```json
{"type":"recruition.getCandidate","version":1,"label":"Get candidate","config":{"connection":"<connection id>","id":"<id>"}}
```

## `recruition.getCurrentUser@1` — Recruition — Get current user

Shows which Recruition user the connection signs in as.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |

Output: `user` object — User · `name` string — Name · `email` string — E-mail address.

```json
{"type":"recruition.getCurrentUser","version":1,"label":"Get current user","config":{"connection":"<connection id>"}}
```

## `recruition.getVacancy@1` — Recruition — Get vacancy

Fetches one Recruition vacancy by id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `id` | text, required | ID: The vacancy's id — a Find vacancies node hands you items[].id. |

Output: `record` any — Record.

```json
{"type":"recruition.getVacancy","version":1,"label":"Get vacancy","config":{"connection":"<connection id>","id":"<id>"}}
```

## `recruition.updateApplication@1` — Recruition — Update application

Moves a Recruition job application to another state.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `id` | text, required | ID: The record's id — a Find node hands you items[].id. |
| `fields` | object (any keys), default `{}` | Fields: One row per column, typed. An empty value is sent as it is — Recruition's own rules decide. |
| `data` | any JSON, optional | Whole body (JSON): The whole request body as JSON, for a shape the rows above cannot hold. When this is filled, the rows are ignored. |
| `state` | text, default `""` | State: The state id, from Recruition's own states list — the Find states node returns them. |

Output: `record` any — Recruition answer.

```json
{"type":"recruition.updateApplication","version":1,"label":"Update application","config":{"fields":{},"state":"","connection":"<connection id>","id":"<id>"}}
```

## `recruition.updateCandidate@1` — Recruition — Update candidate

Changes a Recruition candidate's fields.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `id` | text, required | ID: The record's id — a Find node hands you items[].id. |
| `fields` | object (any keys), default `{}` | Fields: One row per column, typed. An empty value is sent as it is — Recruition's own rules decide. |
| `data` | any JSON, optional | Whole body (JSON): The whole request body as JSON, for a shape the rows above cannot hold. When this is filled, the rows are ignored. |

Output: `record` any — Recruition answer.

```json
{"type":"recruition.updateCandidate","version":1,"label":"Update candidate","config":{"fields":{},"connection":"<connection id>","id":"<id>"}}
```

## `recruition.updateVacancy@1` — Recruition — Update vacancy

Changes a Recruition vacancy's fields.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `recruition`) |
| `id` | text, required | ID: The record's id — a Find node hands you items[].id. |
| `fields` | object (any keys), default `{}` | Fields: One row per column, typed. An empty value is sent as it is — Recruition's own rules decide. |
| `data` | any JSON, optional | Whole body (JSON): The whole request body as JSON, for a shape the rows above cannot hold. When this is filled, the rows are ignored. |

Output: `record` any — Recruition answer.

```json
{"type":"recruition.updateVacancy","version":1,"label":"Update vacancy","config":{"fields":{},"connection":"<connection id>","id":"<id>"}}
```


---

## File: references/nodes/schema.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# schema nodes (`schema.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `schema.get@2` — Schema — Get

Looks up a stored schema — the whole document, or just the field you name.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `name` | text, required | Schema: The workspace schema to work on. A run works on a private copy — nothing here changes the stored schema. — a schema name, listed in `requires.schemas` |
| `path` | text, optional | Path: Dot-separated field names, e.g. settings.apiUrl. Leave empty for the whole document. Arrays are not reachable from a schema path — use {{ schema("…").items[0] }} in an expression instead. |

Output: depends on the data.

```json
{"type":"schema.get","version":2,"label":"Get","config":{"name":"<schema>"}}
```

### Older version `schema.get@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `name` | text, required | Schema: The workspace schema to work on. A run works on a private copy — nothing here changes the stored schema. — a schema name, listed in `requires.schemas` |
| `path` | text, optional | Path: Dot-separated field names, e.g. settings.apiUrl. Leave empty for the whole document. Arrays are not reachable from a schema path — use {{ schema("…").items[0] }} in an expression instead. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"name":"<schema>"}
{"name":"<schema>"}
```

## `schema.set@1` — Schema — Set

Replaces a schema's whole document for the rest of this run.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `name` | text, required | Schema: The workspace schema to work on. A run works on a private copy — nothing here changes the stored schema. — a schema name, listed in `requires.schemas` |
| `value` | any JSON, optional | New document: The complete new document, as JSON or one {{ }} expression. The node outputs it, so a downstream node needs no second schema.get. |

Output: depends on the data.

```json
{"type":"schema.set","version":1,"label":"Set","config":{"name":"<schema>"}}
```

## `schema.setPath@1` — Schema — Set path

Sets one field of a schema for the rest of this run.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `name` | text, required | Schema: The workspace schema to work on. A run works on a private copy — nothing here changes the stored schema. — a schema name, listed in `requires.schemas` |
| `path` | text, required | Path: Dot-separated field names, e.g. settings.apiUrl. Missing steps are created unless you switch that off below. |
| `value` | any JSON, optional | Value: JSON, or one {{ }} expression — 42 stays a number, not the text of one. |
| `createMissing` | true/false, default `true` | Create missing steps: Off refuses a path that does not exist yet, so a typo cannot grow a branch. |

Output: depends on the data.

```json
{"type":"schema.setPath","version":1,"label":"Set path","config":{"createMissing":true,"name":"<schema>","path":"<path>"}}
```


---

## File: references/nodes/ses.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Email nodes (`ses.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `ses.send@2` — Send email

Sends an email with a subject, a message and files you attach — from Flowmage, or from your own Amazon SES account.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, default `""` | SES account: Leave empty to send from Flowmage. Pick a connection to send from your own SES account and your own address. — a connection id from `requires.connections` (provider `aws-ses`) |
| `from` | text, default `""` | From: The verified SES identity to send as, e.g. Flowmage <hello@yourcompany.com>. — used when `connection` is not "" |
| `to` | text, required | To: At most 25 recipients across To, Cc and Bcc (SES allows 50). For a list from upstream, use {{ join(trigger.recipients, ", ") }}. |
| `cc` | text, default `""` | Cc |
| `bcc` | text, default `""` | Bcc |
| `replyTo` | text, default `""` | Reply to |
| `subject` | text, required | Subject |
| `body` | text, required | Body: Plain text by default, {{ }} expressions welcome. In HTML mode write the markup itself — nothing is escaped or rewritten, and a mail with HTML only is what leaves (add a text-only branch if you need both parts). With no connection, the mail goes from Flowmage's own address. |
| `bodyFormat` | "text" or "html", default `"text"` | Body format: Plain text is safer and readable everywhere; HTML is sent exactly as written. No text part is derived from HTML. |
| `attachments` | object of text, default `{}` | Attachments: File name → content — use {{ file("report.pdf").base64 }} as the value. Up to 7 MB of attachments in total. |

Output: `sent` boolean — Sent · `messageId` string — SES message id · `from` string — From (empty when Flowmage's own address was used) · `to` array — To addresses · `cc` array — Cc addresses · `bcc` array — Bcc addresses · `replyTo` array — Reply-to addresses · `subject` string — Subject · `bodyPreview` string — Body preview (capped) · `attachmentCount` number — Attachment count · `attachmentNames` array — Attachment file names.

```json
{"type":"ses.send","version":2,"label":"Send email","config":{"connection":"","from":"","cc":"","bcc":"","replyTo":"","bodyFormat":"text","attachments":{},"to":"<to>","subject":"<subject>","body":"<body>"}}
```

### Older version `ses.send@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `to` | text, required | To: At most 5 recipients across To and Cc. For a list from upstream, use {{ join(trigger.recipients, ", ") }}. |
| `cc` | text, default `""` | Cc |
| `subject` | text, required | Subject |
| `body` | text, required | Body: Plain text; {{ }} expressions welcome. The HTML variant is generated for you, and the mail is sent from Flowmage's own address. |
| `attachments` | object of text, default `{}` | Attachments: File name → content — use {{ file("report.pdf").base64 }} as the value. Up to 7 MB of attachments in total. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"cc":"","attachments":{},"to":"<to>","subject":"<subject>","body":"<body>"}
{"connection":"","from":"","cc":"","bcc":"","replyTo":"","bodyFormat":"text","attachments":{},"to":"<to>","subject":"<subject>","body":"<body>"}
```


---

## File: references/nodes/slack.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Slack nodes (`slack.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `slack.addReaction@1` — Slack — Add reaction

Puts an emoji on a Slack message.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel — a value from the vendor's own list |
| `ts` | text, required | Message timestamp: The ts of the message to react to. — its `{{ }}` must give a string |
| `name` | text, required | Emoji: The emoji's name without the colons around it. |

Output: `added` boolean — Added · `alreadyPresent` boolean — It was already there.

```json
{"type":"slack.addReaction","version":1,"label":"Add reaction","config":{"connection":"<connection id>","channel":"<channel>","ts":"<message timestamp>","name":"<emoji>"}}
```

## `slack.createChannel@1` — Slack — Create channel

Opens a new Slack channel for a deal, ticket or candidate.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `name` | text, required | Channel name: Lower-case letters, digits, hyphens and underscores, at most 80 characters. Capitals are lower-cased for you. — its `{{ }}` must give a string |
| `isPrivate` | true/false, default `false` | Make it private: A private channel is invisible to anyone who is not invited. |
| `teamId` | text, default `""` | Workspace id: Only on an Enterprise Grid org, to say which workspace it belongs to. — its `{{ }}` must give a string |

Output: `channelId` string — Channel id · `channel` object — Channel.

```json
{"type":"slack.createChannel","version":1,"label":"Create channel","config":{"isPrivate":false,"teamId":"","connection":"<connection id>","name":"<channel name>"}}
```

## `slack.deleteMessage@1` — Slack — Delete message

Removes a message from a Slack channel.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel — a value from the vendor's own list |
| `ts` | text, required | Message timestamp: The ts Send message answered for the message to remove. — its `{{ }}` must give a string |

Output: `deleted` boolean — Deleted · `alreadyGone` boolean — It was already gone.

```json
{"type":"slack.deleteMessage","version":1,"label":"Delete message","config":{"connection":"<connection id>","channel":"<channel>","ts":"<message timestamp>"}}
```

## `slack.findUserByEmail@1` — Slack — Find user by e-mail

Looks up a Slack account by its e-mail address.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `email` | text, required | E-mail address: The address the person signs in to Slack with. This needs the users:read.email scope on the app. — its `{{ }}` must give a string |
| `failIfNotFound` | true/false, default `true` | Stop the run when nobody matches: Turn it off to get found: false instead, so the flow can decide what to do next. A deactivated account also reads as not found. |

Output: `user` object — Person · `found` boolean — Somebody matched.

```json
{"type":"slack.findUserByEmail","version":1,"label":"Find user by e-mail","config":{"failIfNotFound":true,"connection":"<connection id>","email":"<e-mail address>"}}
```

## `slack.getChannel@1` — Slack — Get channel

Reads one Slack conversation, with its topic, purpose and settings.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel: A channel id like C0123ABCD. The List channels node gives you the id. — a value from the vendor's own list |
| `includeNumMembers` | true/false, default `false` | Count the members too |

Output: `channel` object — Channel.

```json
{"type":"slack.getChannel","version":1,"label":"Get channel","config":{"includeNumMembers":false,"connection":"<connection id>","channel":"<channel>"}}
```

## `slack.getUser@1` — Slack — Get user

Reads one person's Slack profile.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `user` | text, required | Person: A user id. List channel members and Find user by e-mail both answer one. — a value from the vendor's own list |
| `includeLocale` | true/false, default `false` | Include their language setting |

Output: `user` object — Person.

```json
{"type":"slack.getUser","version":1,"label":"Get user","config":{"includeLocale":false,"connection":"<connection id>","user":"<person>"}}
```

## `slack.inviteToChannel@1` — Slack — Invite people to a channel

Adds people to a Slack channel.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel — a value from the vendor's own list |
| `users` | text, required | People: Pick people, or type user ids — at most 1,000. Find user by e-mail gives you an id. People who are already in the channel are skipped. — a value from the vendor's own list |

Output: `invitedCount` number — People invited · `alreadyPresent` boolean — They were already in the channel · `channel` object — Channel.

```json
{"type":"slack.inviteToChannel","version":1,"label":"Invite people to a channel","config":{"connection":"<connection id>","channel":"<channel>","users":"<people>"}}
```

## `slack.listChannelMembers@2` — Slack — List channel members

Lists who is in a Slack channel.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel: The answer is a list of user ids. Feed one to the Get user node for a name or an e-mail address. — a value from the vendor's own list |
| `limit` | number, default `200` | People per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Slack's page cursor, and refuses past 5,000 people rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `members` array — User ids · `memberCount` number — People in this answer · `hasMore` boolean — More people are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"slack.listChannelMembers","version":2,"label":"List channel members","config":{"limit":200,"returnAll":false,"cursor":"","connection":"<connection id>","channel":"<channel>"}}
```

### Older version `slack.listChannelMembers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel: The answer is a list of user ids. Feed one to the Get user node for a name or an e-mail address. — a value from the vendor's own list |
| `limit` | number, default `200` | People per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Slack's page cursor, and refuses past 5,000 people rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":200,"returnAll":false,"connection":"<connection id>","channel":"<channel>"}
{"limit":200,"returnAll":false,"cursor":"","connection":"<connection id>","channel":"<channel>"}
```

## `slack.listChannels@2` — Slack — List channels

Lists the Slack conversations this app can see.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `types` | text, default `"public_channel"` | Kinds of conversation: A comma list. A private channel only appears when the bot is a member of it. |
| `excludeArchived` | true/false, default `true` | Leave archived channels out |
| `limit` | number, default `200` | Channels per call: 1 to 1000. Slack recommends 100 to 200. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Slack's page cursor, and refuses past 5,000 channels rather than answering a short list. |
| `teamId` | text, default `""` | Workspace id: Only on an Enterprise Grid org, to read one workspace's channels. — its `{{ }}` must give a string |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `channels` array — Channels · `channelCount` number — Channels in this answer · `hasMore` boolean — More channels are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"slack.listChannels","version":2,"label":"List channels","config":{"types":"public_channel","excludeArchived":true,"limit":200,"returnAll":false,"teamId":"","cursor":"","connection":"<connection id>"}}
```

### Older version `slack.listChannels@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `types` | text, default `"public_channel"` | Kinds of conversation: A comma list. A private channel only appears when the bot is a member of it. |
| `excludeArchived` | true/false, default `true` | Leave archived channels out |
| `limit` | number, default `200` | Channels per call: 1 to 1000. Slack recommends 100 to 200. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Slack's page cursor, and refuses past 5,000 channels rather than answering a short list. |
| `teamId` | text, default `""` | Workspace id: Only on an Enterprise Grid org, to read one workspace's channels. — its `{{ }}` must give a string |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"types":"public_channel","excludeArchived":true,"limit":200,"returnAll":false,"teamId":"","connection":"<connection id>"}
{"types":"public_channel","excludeArchived":true,"limit":200,"returnAll":false,"teamId":"","cursor":"","connection":"<connection id>"}
```

## `slack.listMessages@2` — Slack — List messages

Reads the recent messages of a Slack channel.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel: If your Slack app was installed from a listing rather than built in your own workspace, Slack caps this method at one call a minute and 15 messages. — a value from the vendor's own list |
| `oldest` | text, default `""` | From (message timestamp): Only messages after this ts. Leave empty for the most recent ones. — its `{{ }}` must give a string |
| `latest` | text, default `""` | Until (message timestamp) — its `{{ }}` must give a string |
| `inclusive` | true/false, default `false` | Include the messages at those timestamps |
| `limit` | number, default `100` | Messages per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Slack's page cursor, and refuses past 5,000 messages rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `messages` array — Messages · `messageCount` number — Messages in this answer · `hasMore` boolean — More messages are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"slack.listMessages","version":2,"label":"List messages","config":{"oldest":"","latest":"","inclusive":false,"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","channel":"<channel>"}}
```

### Older version `slack.listMessages@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel: If your Slack app was installed from a listing rather than built in your own workspace, Slack caps this method at one call a minute and 15 messages. — a value from the vendor's own list |
| `oldest` | text, default `""` | From (message timestamp): Only messages after this ts. Leave empty for the most recent ones. — its `{{ }}` must give a string |
| `latest` | text, default `""` | Until (message timestamp) — its `{{ }}` must give a string |
| `inclusive` | true/false, default `false` | Include the messages at those timestamps |
| `limit` | number, default `100` | Messages per call: 1 to 999. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Slack's page cursor, and refuses past 5,000 messages rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"oldest":"","latest":"","inclusive":false,"limit":100,"returnAll":false,"connection":"<connection id>","channel":"<channel>"}
{"oldest":"","latest":"","inclusive":false,"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","channel":"<channel>"}
```

## `slack.listThreadReplies@2` — Slack — List thread replies

Reads the replies under one Slack message.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel — a value from the vendor's own list |
| `ts` | text, required | Thread timestamp: The ts of the message the thread hangs under. The first message in the answer is that parent, not a reply. — its `{{ }}` must give a string |
| `limit` | number, default `100` | Messages per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Slack's page cursor, and refuses past 5,000 messages rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `messages` array — Messages, the parent first · `messageCount` number — Messages in this answer · `hasMore` boolean — More messages are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"slack.listThreadReplies","version":2,"label":"List thread replies","config":{"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","channel":"<channel>","ts":"<thread timestamp>"}}
```

### Older version `slack.listThreadReplies@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel — a value from the vendor's own list |
| `ts` | text, required | Thread timestamp: The ts of the message the thread hangs under. The first message in the answer is that parent, not a reply. — its `{{ }}` must give a string |
| `limit` | number, default `100` | Messages per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Slack's page cursor, and refuses past 5,000 messages rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":100,"returnAll":false,"connection":"<connection id>","channel":"<channel>","ts":"<thread timestamp>"}
{"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","channel":"<channel>","ts":"<thread timestamp>"}
```

## `slack.listUsers@2` — Slack — List users

Lists the people in a Slack workspace.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `limit` | number, default `200` | People per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Slack's page cursor, and refuses past 5,000 people rather than answering a short list. |
| `teamId` | text, default `""` | Workspace id: Only on an Enterprise Grid org, to read one workspace's people. — its `{{ }}` must give a string |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `users` array — People · `userCount` number — People in this answer · `hasMore` boolean — More people are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"slack.listUsers","version":2,"label":"List users","config":{"limit":200,"returnAll":false,"teamId":"","cursor":"","connection":"<connection id>"}}
```

### Older version `slack.listUsers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `limit` | number, default `200` | People per call |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Slack's page cursor, and refuses past 5,000 people rather than answering a short list. |
| `teamId` | text, default `""` | Workspace id: Only on an Enterprise Grid org, to read one workspace's people. — its `{{ }}` must give a string |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"limit":200,"returnAll":false,"teamId":"","connection":"<connection id>"}
{"limit":200,"returnAll":false,"teamId":"","cursor":"","connection":"<connection id>"}
```

## `slack.openDirectMessage@1` — Slack — Open a direct message

Starts a private conversation with one person or a small group.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `users` | text, required | People: One person for a private conversation, or up to 8 for a group — pick them, or type user ids. Pass the channel id this node answers to Send message. — a value from the vendor's own list |

Output: `channelId` string — Channel id — pass it to Send message · `alreadyOpen` boolean — The conversation already existed.

```json
{"type":"slack.openDirectMessage","version":1,"label":"Open a direct message","config":{"connection":"<connection id>","users":"<people>"}}
```

## `slack.pinMessage@1` — Slack — Pin message

Pins a Slack message to the top of its channel.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel — a value from the vendor's own list |
| `ts` | text, required | Message timestamp: The ts of the message to pin. A channel holds a limited number of pins. — its `{{ }}` must give a string |

Output: `pinned` boolean — Pinned · `alreadyPresent` boolean — It was already pinned.

```json
{"type":"slack.pinMessage","version":1,"label":"Pin message","config":{"connection":"<connection id>","channel":"<channel>","ts":"<message timestamp>"}}
```

## `slack.scheduleMessage@1` — Slack — Schedule message

Hands Slack a message to post at a time you choose.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel — a value from the vendor's own list |
| `postAt` | text, required | Post it at: An ISO time like 2026-10-01T09:00:00Z, or a Unix second count. Slack accepts up to 120 days ahead, and never a time in the past. — its `{{ }}` must give a string |
| `messageFormat` | "text" or "blocks", default `"text"` | Message format |
| `text` | text, default `""` | Message |
| `blocks` | text, default `""` | Blocks — used when `messageFormat` is "blocks" |
| `threadTs` | text, default `""` | Reply to (thread timestamp) — its `{{ }}` must give a string |

Output: `scheduledMessageId` string — Scheduled message id · `postAt` number — When Slack will post it (Unix seconds) · `channel` string — Channel id.

```json
{"type":"slack.scheduleMessage","version":1,"label":"Schedule message","config":{"messageFormat":"text","text":"","blocks":"","threadTs":"","connection":"<connection id>","channel":"<channel>","postAt":"<post it at>"}}
```

## `slack.sendEphemeralMessage@1` — Slack — Send message to one person

Posts a message only one person in a Slack channel can see.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel: The channel the person sees it in. They have to be a member of it. — a value from the vendor's own list |
| `user` | text, required | Person: The user id who may see it — Find user by e-mail gives you one. — a value from the vendor's own list |
| `messageFormat` | "text" or "blocks", default `"text"` | Message format |
| `text` | text, default `""` | Message |
| `blocks` | text, default `""` | Blocks — used when `messageFormat` is "blocks" |
| `threadTs` | text, default `""` | Reply to (thread timestamp) — its `{{ }}` must give a string |

Output: `messageTs` string — Message timestamp.

```json
{"type":"slack.sendEphemeralMessage","version":1,"label":"Send message to one person","config":{"messageFormat":"text","text":"","blocks":"","threadTs":"","connection":"<connection id>","channel":"<channel>","user":"<person>"}}
```

## `slack.sendMessage@1` — Slack — Send message

Posts a message to a Slack channel, direct message or thread.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel: A channel id like C0123ABCD, a channel name like #releases, or a user id for a direct message. The List channels node gives you the id. The bot must be in a private channel before it can post there. — a value from the vendor's own list |
| `messageFormat` | "text" or "blocks", default `"text"` | Message format |
| `text` | text, default `""` | Message: Slack markdown: *bold*, _italic_, `code`, <https://example.com\|link text>, <@U0123> to mention someone. With Block Kit layout this text becomes the notification line and what clients that cannot draw blocks show. |
| `blocks` | text, default `""` | Blocks: Block Kit layout, as a list. At most 50 blocks. Slack's own Block Kit Builder composes one for you. — used when `messageFormat` is "blocks" |
| `threadTs` | text, default `""` | Reply to (thread timestamp): The ts of the message to reply under. Send message answers a ts. — its `{{ }}` must give a string |
| `replyBroadcast` | true/false, default `false` | Also show in the channel — used when `threadTs` is not "" |
| `unfurlLinks` | true/false, default `true` | Show previews for links |
| `unfurlMedia` | true/false, default `true` | Show previews for images and video |
| `linkNames` | true/false, default `false` | Turn @name and #channel into links |

Output: `ts` string — Message timestamp — use it to update, delete, react or reply · `channel` string — Channel id · `message` object — Message.

```json
{"type":"slack.sendMessage","version":1,"label":"Send message","config":{"messageFormat":"text","text":"","blocks":"","threadTs":"","replyBroadcast":false,"unfurlLinks":true,"unfurlMedia":true,"linkNames":false,"connection":"<connection id>","channel":"<channel>"}}
```

## `slack.updateMessage@1` — Slack — Update message

Rewrites a message Slack already posted.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `channel` | text, required | Channel: The channel the message sits in — Send message answers it. — a value from the vendor's own list |
| `ts` | text, required | Message timestamp: The ts Send message answered for the message to rewrite. — its `{{ }}` must give a string |
| `messageFormat` | "text" or "blocks", default `"text"` | Message format |
| `text` | text, default `""` | Message: The new text. With Block Kit layout this becomes the notification line instead. |
| `blocks` | text, default `""` | Blocks: The new Block Kit layout, as a list. At most 50 blocks. — used when `messageFormat` is "blocks" |
| `linkNames` | true/false, default `false` | Turn @name and #channel into links |

Output: `ts` string — Message timestamp · `channel` string — Channel id · `message` object — Message.

```json
{"type":"slack.updateMessage","version":1,"label":"Update message","config":{"messageFormat":"text","text":"","blocks":"","linkNames":false,"connection":"<connection id>","channel":"<channel>","ts":"<message timestamp>"}}
```

## `slack.uploadFile@1` — Slack — Upload file

Uploads a file to Slack and shares it in a channel or thread.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `slack`) |
| `name` | text, required | File name: The file's name, extension included. Pick the file, then paste its base64 below. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("name").base64 }}. Uploads over the "Use fixed IP address" lane are unverified; leave that toggle off for this node. — its `{{ }}` must give a string |
| `channelId` | text, default `""` | Share it in: A channel id. Leave it empty to upload the file without posting it anywhere — it then only exists in Slack's file list. — a value from the vendor's own list |
| `threadTs` | text, default `""` | Reply to (thread timestamp) — its `{{ }}` must give a string; used when `channelId` is not "" |
| `title` | text, default `""` | Title — its `{{ }}` must give a string |
| `initialComment` | text, default `""` | Message with the file: Posted above the file in the channel. |
| `snippetType` | text, default `""` | Code snippet language: Only for a text snippet — it decides how Slack highlights it. |
| `altText` | text, default `""` | Description for screen readers — its `{{ }}` must give a string |

Output: `fileId` string — File id · `file` object — File · `permalink` string — Open in Slack.

```json
{"type":"slack.uploadFile","version":1,"label":"Upload file","config":{"channelId":"","threadTs":"","title":"","initialComment":"","snippetType":"","altText":"","connection":"<connection id>","name":"<file name>","contentBase64":"<file contents>"}}
```


---

## File: references/nodes/soap.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# SOAP web service nodes (`soap.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `soap.callOperation@1` — SOAP web service — Call an operation

Calls one operation of your own SOAP web service and returns its answer.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `soap`) |
| `operation` | text, required | Operation: The operations the connection read from the service's description when it was tested. Test the connection again to see one the service added since. — a value from the list the connection's Test read |
| `parameters` | object (any keys), default `{}` | Parameters: The operation's parameters by the names the service uses, such as { "werknemerId": 12 }. A field left out is not sent; null sends it empty. An update usually REPLACES the record: read it first, change what you need, and send the whole record back. |
| `changes` | "reads" or "changes", default `"changes"` | What this operation does: A read may be re-run freely. Choose the other one for any operation that creates, updates or sends something: run twice, it could create two records. |

Output: `result` any — What the service answered.

```json
{"type":"soap.callOperation","version":1,"label":"Call an operation","config":{"parameters":{},"changes":"changes","connection":"<connection id>","operation":"<operation>"}}
```


---

## File: references/nodes/table.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# table nodes (`table.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `table.delete@1` — Table — Delete

Deletes one row from a table — there is no undo.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `tableId` | text, required | Table: Rows are separate per environment. Editor runs — Test node and Execute — read and write the test copy. — a table id from `requires.tables` |
| `rowId` | text, required | Row id: Pick the row with Table — Select first, then loop over its rows. Its id is under rows[].rowId. A retry of this step never deletes twice — it gets the first delete's answer back. A row that is already deleted fails the step with TABLE_ROW_NOT_FOUND. |
| `expectedVersion` | number, optional | Expected version: Refuses when someone changed the row in between (optimistic concurrency). |

Output: `rowId` string — Row id · `rowVersion` number — Row version.

```json
{"type":"table.delete","version":1,"label":"Delete","config":{"tableId":"<table>","rowId":"<row id>"}}
```

## `table.insert@1` — Table — Insert

Adds a new row to a workspace table.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `tableId` | text, required | Table: Rows are separate per environment. Editor runs — Test node and Execute — read and write the test copy. — a table id from `requires.tables` |
| `values` | object (any keys), default `{}` | Values: One row per column of the table; values accept {{ }} expressions, and large content belongs in a file. |

Output: `rowId` string — Row id · `rowVersion` number — Row version.

```json
{"type":"table.insert","version":1,"label":"Insert","config":{"values":{},"tableId":"<table>"}}
```

## `table.select@2` — Table — Select

Finds the rows in a table that match your filters, in the order you sort them.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `tableId` | text, required | Table: Rows are separate per environment. Editor runs — Test node and Execute — read and write the test copy. — a table id from `requires.tables` |
| `filters` | list of {column: text, operator: text, value?: any JSON}, default `[]` | Rows that match: Every filter must match; values accept {{ }} expressions. |
| `sort` | list of {column: text, direction: "asc" or "desc"}, default `[]` | Sort: Without a sort, rows come back oldest first. |
| `giveBack` | "all" or "first", default `"all"` | Give back |
| `atMost` | number, default `100` | At most: Past this many, the first ones come back with a nextCursor for the rest. — used when `giveBack` is "all" |
| `ifNone` | "empty" or "fail", default `"empty"` | If nothing matches |
| `search` | text, default `""` | Search: Free text over the text and date columns, on top of the filters — a table with neither matches nothing. |
| `ifMoreRows` | "first" or "fail", default `"first"` | If more rows: What happens when more rows match than At most allows. — used when `giveBack` is "all" |
| `cursor` | text, default `""` | Cursor: Continue a previous select from its nextCursor. |

Output: `rows` array — Rows · `nextCursor` string — Cursor for the next page · `filteredCount` number — Rows matching the filter · `totalCount` number — Rows in the table · `tableChanged` boolean — The table changed while it was read — a row may be missing.

```json
{"type":"table.select","version":2,"label":"Select","config":{"filters":[],"sort":[],"giveBack":"all","atMost":100,"ifNone":"empty","search":"","ifMoreRows":"first","cursor":"","tableId":"<table>"}}
```

### Older version `table.select@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `tableId` | text, required | Table: Rows are separate per environment. Editor runs — Test node and Execute — read and write the test copy. — a table id from `requires.tables` |
| `filters` | list of {column: text, operator: text, value?: any JSON}, default `[]` | Filters: Every filter must match; values accept {{ }} expressions. |
| `sort` | list of {column: text, direction: "asc" or "desc"}, default `[]` | Sort: Order rows before they are returned. Without a sort, rows come back oldest first. |
| `search` | text, default `""` | Search: Free text over the text and date columns — a table with neither matches nothing. |
| `maxRows` | number, optional | Max rows: Empty means this workspace's limit, which is also the most a select returns. |
| `ifMoreRows` | "fail" or "first", default `"fail"` | If more rows: Return the first rows with a nextCursor to read a large table page by page. |
| `cursor` | text, default `""` | Cursor: Continue a previous select from its nextCursor. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"filters":[],"sort":[],"search":"","ifMoreRows":"fail","cursor":"","tableId":"<table>"}
{"filters":[],"sort":[],"giveBack":"all","atMost":50000,"ifNone":"empty","search":"","ifMoreRows":"fail","cursor":"","tableId":"<table>"}
```

## `table.update@2` — Table — Update

Changes the columns you name on one row that already exists.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `tableId` | text, required | Table: Rows are separate per environment. Editor runs — Test node and Execute — read and write the test copy. — a table id from `requires.tables` |
| `where.column` | text, default `""` |  |
| `where.operator` | text, default `"equals"` |  |
| `where.value` | any JSON, optional |  |
| `values` | object (any keys), default `{}` | Change these columns: Only the columns you list change; values accept {{ }} expressions, and Clear writes a null, which empties the cell. |
| `ifNone` | "fail" or "skip", default `"fail"` | If no row matches |
| `rowId` | text, default `""` | Row id: Instead of Which row: the id of the row to change, as a select returns it. |
| `expectedVersion` | number, optional | Expected version: Refuses when someone changed the row in between (optimistic concurrency). |

Output: `rowId` string — Row id · `rowVersion` number — Row version · `changed` boolean — A row was changed.

```json
{"type":"table.update","version":2,"label":"Update","config":{"where":{"column":"","operator":"equals"},"values":{},"ifNone":"fail","rowId":"","tableId":"<table>"}}
```

### Older version `table.update@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `tableId` | text, required | Table: Rows are separate per environment. Editor runs — Test node and Execute — read and write the test copy. — a table id from `requires.tables` |
| `rowId` | text, required | Row id: Pick the row with Table — Select first, then loop over its rows. Its id is under rows[].rowId. |
| `values` | object (any keys), default `{}` | Values: Only the columns you set change; values accept {{ }} expressions, and Clear writes a null, which empties the cell. |
| `expectedVersion` | number, optional | Expected version: Refuses when someone changed the row in between (optimistic concurrency). |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"values":{},"tableId":"<table>","rowId":"<row id>"}
{"where":{"column":"","operator":"equals"},"values":{},"ifNone":"fail","rowId":"<row id>","tableId":"<table>"}
```

## Filter operators by column type

- text: `equals`, `not_equals`, `contains`, `starts_with`, `ends_with`, `is_empty`, `is_not_empty`
- number: `equals`, `not_equals`, `greater_than`, `greater_than_or_equal`, `less_than`, `less_than_or_equal`, `is_empty`, `is_not_empty`
- boolean: `equals`, `not_equals`
- datetime: `equals`, `before`, `after`, `is_empty`, `is_not_empty`
- json: `contains`, `not_contains`, `is_empty`, `is_not_empty`

`is_empty` and `is_not_empty` take no `value`. A filter's `column` is the column KEY.


---

## File: references/nodes/teamtailor.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Teamtailor nodes (`teamtailor.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `teamtailor.createCandidate@1` — Teamtailor — Create candidate

Adds a candidate to Teamtailor, or merges into the one with that e-mail.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `email` | text, default `""` | E-mail: Required unless Sourced is on — Teamtailor keys candidates by e-mail. |
| `firstName` | text, default `""` | First name |
| `lastName` | text, default `""` | Last name |
| `phone` | text, default `""` | Phone |
| `pitch` | text, default `""` | Pitch: The one-line summary Teamtailor shows under the name. |
| `merge` | true/false, default `true` | Update the person if they already exist: When a candidate with this e-mail already exists, update them instead of creating a second record. Leaving this off means a second run of this flow creates a duplicate. |
| `sourced` | true/false, default `false` | Sourced, not applied: The candidate did not apply — you found them. Teamtailor then allows a candidate without an e-mail. |
| `internal` | true/false, default `false` | An internal candidate |
| `unsubscribed` | true/false, default `false` | Unsubscribed from mailings |
| `resumeUrl` | text, default `""` | CV address: A publicly reachable address. Teamtailor downloads the file and hosts it. For bytes you hold, use the Upload candidate file node instead. |
| `linkedinUrl` | text, default `""` | LinkedIn address |
| `tags` | text, default `""` | Tags |
| `referringSite` | text, default `""` | Referring site |
| `referringUrl` | text, default `""` | Referring address |
| `consentGivenFutureJobs` | true/false, default `false` | Consented to be kept for future jobs |
| `departmentId` | text, default `""` | Department id: The List departments node gives you the id. — a value from the vendor's own list |
| `roleId` | text, default `""` | Role id |
| `divisionId` | text, default `""` | Division id |
| `locationIds` | text, default `""` | Location ids |
| `regionIds` | text, default `""` | Region ids |
| `attributes` | object (any keys), default `{}` | Anything else: Any other Teamtailor candidate attribute, spelled the way Teamtailor spells it — kebab-case, for example linkedin-uid. |

Output: `candidateId` string — Candidate id · `candidate` object — Candidate · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.createCandidate","version":1,"label":"Create candidate","config":{"email":"","firstName":"","lastName":"","phone":"","pitch":"","merge":true,"sourced":false,"internal":false,"unsubscribed":false,"resumeUrl":"","linkedinUrl":"","tags":"","referringSite":"","referringUrl":"","consentGivenFutureJobs":false,"departmentId":"","roleId":"","divisionId":"","locationIds":"","regionIds":"","attributes":{},"connection":"<connection id>"}}
```

## `teamtailor.createJob@1` — Teamtailor — Create job

Opens a job in Teamtailor, optionally from one of your templates.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `title` | text, required | Title |
| `body` | text, required | Description: Teamtailor stores this as HTML — paragraphs and lists come through. |
| `userId` | text, required | Recruiter id: The person Teamtailor puts the job under. It is required; the List users node gives you the id. |
| `pitch` | text, default `""` | Pitch |
| `status` | "draft" or "published" or "unlisted" or "archived" or "scheduled", default `"draft"` | Status |
| `remoteStatus` | "" or "none" or "temporary" or "hybrid" or "fully", default `""` | Remote |
| `tags` | text, default `""` | Tags |
| `templateId` | text, default `""` | Template id: Copies everything from the template — triggers, the application form. Anything you fill in here overwrites the template's value. Find template ids with List jobs and the status filter set to Every status. |
| `departmentId` | text, default `""` | Department id: The List departments node gives you the id. — a value from the vendor's own list |
| `roleId` | text, default `""` | Role id |
| `divisionId` | text, default `""` | Division id |
| `locationIds` | text, default `""` | Location ids |
| `regionIds` | text, default `""` | Region ids |

Output: `jobId` string — Job id · `job` object — Job · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.createJob","version":1,"label":"Create job","config":{"pitch":"","status":"draft","remoteStatus":"","tags":"","templateId":"","departmentId":"","roleId":"","divisionId":"","locationIds":"","regionIds":"","connection":"<connection id>","title":"<title>","body":"<description>","userId":"<recruiter id>"}}
```

## `teamtailor.createJobApplication@1` — Teamtailor — Apply candidate to job

Puts an existing Teamtailor candidate on a job's pipeline.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `candidateId` | text, required | Candidate id: The candidate has to exist already — Create candidate makes one. |
| `jobId` | text, required | Job id: The List jobs node gives you the id. |
| `coverLetter` | text, default `""` | Cover letter |
| `sourced` | true/false, default `false` | Sourced, not applied: Marks the application as one you created, not one the candidate sent. |
| `sendUserNotifications` | true/false, default `true` | Tell the recruiters: Tells the job's recruiters that someone applied. |

Output: `jobApplicationId` string — Application id · `jobApplication` object — Application · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.createJobApplication","version":1,"label":"Apply candidate to job","config":{"coverLetter":"","sourced":false,"sendUserNotifications":true,"connection":"<connection id>","candidateId":"<candidate id>","jobId":"<job id>"}}
```

## `teamtailor.createNote@1` — Teamtailor — Add note or review

Writes a note, or a 1–5 review, on a Teamtailor candidate.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `note` | text, required | Note: What the recruiters read where they work. |
| `candidateId` | text, required | Candidate id: The List candidates node gives you the id. |
| `userId` | text, required | Author id: Teamtailor records the note under this person — a note needs an author. The List users node gives you the id. |
| `rating` | number, default `0` | Rating: 1 to 5 turns the note into a review. Zero leaves it a plain comment, which is the default. |
| `jobApplicationId` | text, default `""` | Application id: Links the note to one application instead of to the candidate in general. |

Output: `noteId` string — Note id · `note` object — Note · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.createNote","version":1,"label":"Add note or review","config":{"rating":0,"jobApplicationId":"","connection":"<connection id>","note":"<note>","candidateId":"<candidate id>","userId":"<author id>"}}
```

## `teamtailor.getCandidate@1` — Teamtailor — Get candidate

Fetches one Teamtailor candidate, with the related records you ask for.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `candidateId` | text, required | Candidate id: The List candidates node gives you the id. |
| `include` | text, default `"job-applications"` | Also fetch: Related records to fetch with the candidate, comma separated. They arrive folded into the answer, so {{ candidate.department.name }} works. |

Output: `candidate` object — Candidate · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.getCandidate","version":1,"label":"Get candidate","config":{"include":"job-applications","connection":"<connection id>","candidateId":"<candidate id>"}}
```

## `teamtailor.getJob@1` — Teamtailor — Get job

Fetches one Teamtailor job with its department, role and locations.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `jobId` | text, required | Job id: The List jobs node gives you the id. |
| `include` | text, default `"department,role,locations"` | Also fetch: Related records to fetch with the job, comma separated. |

Output: `job` object — Job · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.getJob","version":1,"label":"Get job","config":{"include":"department,role,locations","connection":"<connection id>","jobId":"<job id>"}}
```

## `teamtailor.listCandidates@2` — Teamtailor — List candidates

Finds Teamtailor candidates by e-mail, department, location or date.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `email` | text, default `""` | E-mail: Teamtailor keys candidates by e-mail — this is the exact-match lookup. |
| `department` | text, default `""` | Department id — a value from the vendor's own list |
| `division` | text, default `""` | Division id |
| `role` | text, default `""` | Role id |
| `locations` | text, default `""` | Location ids |
| `regions` | text, default `""` | Region ids |
| `city` | text, default `""` | City |
| `country` | text, default `""` | Country |
| `state` | text, default `""` | State |
| `phone` | text, default `""` | Phone |
| `connected` | "" or "true" or "false", default `""` | Connected |
| `createdFrom` | text, default `""` | Created from |
| `createdTo` | text, default `""` | Created up to |
| `updatedFrom` | text, default `""` | Changed from: The filter a nightly sync reads — everything touched since this moment. |
| `updatedTo` | text, default `""` | Changed up to |
| `include` | text, default `""` | Also fetch: Related records to fetch with each candidate, comma separated. They arrive folded into each row, so {{ candidate.department.name }} works. |
| `sort` | text, default `"-id"` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Candidates per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 candidates rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Candidates · `itemCount` number — Candidates in this answer · `recordCount` number — Candidates matching the filter (all pages) · `hasMore` boolean — More candidates are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listCandidates","version":2,"label":"List candidates","config":{"email":"","department":"","division":"","role":"","locations":"","regions":"","city":"","country":"","state":"","phone":"","connected":"","createdFrom":"","createdTo":"","updatedFrom":"","updatedTo":"","include":"","sort":"-id","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listCandidates@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `email` | text, default `""` | E-mail: Teamtailor keys candidates by e-mail — this is the exact-match lookup. |
| `department` | text, default `""` | Department id — a value from the vendor's own list |
| `division` | text, default `""` | Division id |
| `role` | text, default `""` | Role id |
| `locations` | text, default `""` | Location ids |
| `regions` | text, default `""` | Region ids |
| `city` | text, default `""` | City |
| `country` | text, default `""` | Country |
| `state` | text, default `""` | State |
| `phone` | text, default `""` | Phone |
| `connected` | "" or "true" or "false", default `""` | Connected |
| `createdFrom` | text, default `""` | Created from |
| `createdTo` | text, default `""` | Created up to |
| `updatedFrom` | text, default `""` | Changed from: The filter a nightly sync reads — everything touched since this moment. |
| `updatedTo` | text, default `""` | Changed up to |
| `include` | text, default `""` | Also fetch: Related records to fetch with each candidate, comma separated. They arrive folded into each row, so {{ candidate.department.name }} works. |
| `sort` | text, default `"-id"` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Candidates per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 candidates rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"email":"","department":"","division":"","role":"","locations":"","regions":"","city":"","country":"","state":"","phone":"","connected":"","createdFrom":"","createdTo":"","updatedFrom":"","updatedTo":"","include":"","sort":"-id","pageSize":30,"returnAll":false,"connection":"<connection id>"}
{"email":"","department":"","division":"","role":"","locations":"","regions":"","city":"","country":"","state":"","phone":"","connected":"","createdFrom":"","createdTo":"","updatedFrom":"","updatedTo":"","include":"","sort":"-id","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listCustomFieldValues@2` — Teamtailor — List custom field values

Lists the values stored in Teamtailor custom fields.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `customFieldId` | text, default `""` | Custom field id: The List custom fields node gives you the id. |
| `fieldType` | text, default `""` | Field type: For example CustomField::Text or CustomField::Select. |
| `include` | text, default `"custom-field,owner"` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 values rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Values · `itemCount` number — Values in this answer · `recordCount` number — Values matching the filter (all pages) · `hasMore` boolean — More rows are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listCustomFieldValues","version":2,"label":"List custom field values","config":{"customFieldId":"","fieldType":"","include":"custom-field,owner","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listCustomFieldValues@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `customFieldId` | text, default `""` | Custom field id: The List custom fields node gives you the id. |
| `fieldType` | text, default `""` | Field type: For example CustomField::Text or CustomField::Select. |
| `include` | text, default `"custom-field,owner"` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 values rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"customFieldId":"","fieldType":"","include":"custom-field,owner","sort":"","pageSize":30,"returnAll":false,"connection":"<connection id>"}
{"customFieldId":"","fieldType":"","include":"custom-field,owner","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listCustomFields@2` — Teamtailor — List custom fields

Lists Teamtailor custom fields, with their types and ids.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `include` | text, default `""` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 fields rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Custom fields · `itemCount` number — Custom fields in this answer · `recordCount` number — Custom fields matching the filter (all pages) · `hasMore` boolean — More rows are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listCustomFields","version":2,"label":"List custom fields","config":{"include":"","sort":"","pageSize":30,"returnAll":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listCustomFields@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `include` | text, default `""` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 fields rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"include":"","sort":"","pageSize":30,"returnAll":true,"connection":"<connection id>"}
{"include":"","sort":"","pageSize":30,"returnAll":true,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listDepartments@2` — Teamtailor — List departments

Lists Teamtailor departments and their roles.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `include` | text, default `"roles"` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 departments rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Departments · `itemCount` number — Departments in this answer · `recordCount` number — Departments matching the filter (all pages) · `hasMore` boolean — More rows are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listDepartments","version":2,"label":"List departments","config":{"include":"roles","sort":"","pageSize":30,"returnAll":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listDepartments@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `include` | text, default `"roles"` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 departments rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"include":"roles","sort":"","pageSize":30,"returnAll":true,"connection":"<connection id>"}
{"include":"roles","sort":"","pageSize":30,"returnAll":true,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listJobApplications@2` — Teamtailor — List job applications

Lists Teamtailor applications, filtered by stage type and dates.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `stageType` | "" or "Inbox" or "In process" or "Hired", default `""` | Stage type: The four stage types Teamtailor groups every pipeline column under. |
| `createdFrom` | text, default `""` | Applied from |
| `createdTo` | text, default `""` | Applied up to |
| `updatedFrom` | text, default `""` | Changed from |
| `updatedTo` | text, default `""` | Changed up to |
| `changedStageFrom` | text, default `""` | Moved stage from: The filter a reconciling sync reads — everything that moved since this moment. |
| `changedStageTo` | text, default `""` | Moved stage up to |
| `include` | text, default `"candidate,stage,job"` | Also fetch: Related records to fetch with each application, comma separated. Without them an application is three ids. |
| `sort` | text, default `"-changed_stage_at"` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Applications per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 applications rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Applications · `itemCount` number — Applications in this answer · `recordCount` number — Applications matching the filter (all pages) · `hasMore` boolean — More applications are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listJobApplications","version":2,"label":"List job applications","config":{"stageType":"","createdFrom":"","createdTo":"","updatedFrom":"","updatedTo":"","changedStageFrom":"","changedStageTo":"","include":"candidate,stage,job","sort":"-changed_stage_at","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listJobApplications@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `stageType` | "" or "Inbox" or "In process" or "Hired", default `""` | Stage type: The four stage types Teamtailor groups every pipeline column under. |
| `createdFrom` | text, default `""` | Applied from |
| `createdTo` | text, default `""` | Applied up to |
| `updatedFrom` | text, default `""` | Changed from |
| `updatedTo` | text, default `""` | Changed up to |
| `changedStageFrom` | text, default `""` | Moved stage from: The filter a reconciling sync reads — everything that moved since this moment. |
| `changedStageTo` | text, default `""` | Moved stage up to |
| `include` | text, default `"candidate,stage,job"` | Also fetch: Related records to fetch with each application, comma separated. Without them an application is three ids. |
| `sort` | text, default `"-changed_stage_at"` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Applications per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 applications rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"stageType":"","createdFrom":"","createdTo":"","updatedFrom":"","updatedTo":"","changedStageFrom":"","changedStageTo":"","include":"candidate,stage,job","sort":"-changed_stage_at","pageSize":30,"returnAll":false,"connection":"<connection id>"}
{"stageType":"","createdFrom":"","createdTo":"","updatedFrom":"","updatedTo":"","changedStageFrom":"","changedStageTo":"","include":"candidate,stage,job","sort":"-changed_stage_at","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listJobs@2` — Teamtailor — List jobs

Lists Teamtailor jobs, published or internal, with their departments.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `status` | "published" or "unlisted" or "archived" or "draft" or "scheduled" or "all", default `"published"` | Status: Teamtailor answers only published jobs unless you say otherwise here. |
| `feed` | "public" or "internal", default `"public"` | Feed |
| `department` | text, default `""` | Department id — a value from the vendor's own list |
| `division` | text, default `""` | Division id |
| `role` | text, default `""` | Role id |
| `locations` | text, default `""` | Location ids |
| `regions` | text, default `""` | Region ids |
| `tags` | text, default `""` | Tags |
| `remoteStatus` | "" or "none" or "temporary" or "hybrid" or "fully", default `""` | Remote |
| `templateName` | text, default `""` | Template name: Finds the jobs made from one template. |
| `createdFrom` | text, default `""` | Created from |
| `createdTo` | text, default `""` | Created up to |
| `updatedFrom` | text, default `""` | Changed from |
| `updatedTo` | text, default `""` | Changed up to |
| `include` | text, default `"department,locations"` | Also fetch: Related records to fetch with each job, comma separated. |
| `sort` | text, default `""` | Sort by: Teamtailor's own order is -pinned,date. A minus in front turns the order around. |
| `pageSize` | number, default `30` | Jobs per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 jobs rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Jobs · `itemCount` number — Jobs in this answer · `recordCount` number — Jobs matching the filter (all pages) · `hasMore` boolean — More jobs are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listJobs","version":2,"label":"List jobs","config":{"status":"published","feed":"public","department":"","division":"","role":"","locations":"","regions":"","tags":"","remoteStatus":"","templateName":"","createdFrom":"","createdTo":"","updatedFrom":"","updatedTo":"","include":"department,locations","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listJobs@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `status` | "published" or "unlisted" or "archived" or "draft" or "scheduled" or "all", default `"published"` | Status: Teamtailor answers only published jobs unless you say otherwise here. |
| `feed` | "public" or "internal", default `"public"` | Feed |
| `department` | text, default `""` | Department id — a value from the vendor's own list |
| `division` | text, default `""` | Division id |
| `role` | text, default `""` | Role id |
| `locations` | text, default `""` | Location ids |
| `regions` | text, default `""` | Region ids |
| `tags` | text, default `""` | Tags |
| `remoteStatus` | "" or "none" or "temporary" or "hybrid" or "fully", default `""` | Remote |
| `templateName` | text, default `""` | Template name: Finds the jobs made from one template. |
| `createdFrom` | text, default `""` | Created from |
| `createdTo` | text, default `""` | Created up to |
| `updatedFrom` | text, default `""` | Changed from |
| `updatedTo` | text, default `""` | Changed up to |
| `include` | text, default `"department,locations"` | Also fetch: Related records to fetch with each job, comma separated. |
| `sort` | text, default `""` | Sort by: Teamtailor's own order is -pinned,date. A minus in front turns the order around. |
| `pageSize` | number, default `30` | Jobs per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 jobs rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"status":"published","feed":"public","department":"","division":"","role":"","locations":"","regions":"","tags":"","remoteStatus":"","templateName":"","createdFrom":"","createdTo":"","updatedFrom":"","updatedTo":"","include":"department,locations","sort":"","pageSize":30,"returnAll":false,"connection":"<connection id>"}
{"status":"published","feed":"public","department":"","division":"","role":"","locations":"","regions":"","tags":"","remoteStatus":"","templateName":"","createdFrom":"","createdTo":"","updatedFrom":"","updatedTo":"","include":"department,locations","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listLocations@2` — Teamtailor — List locations

Lists Teamtailor locations and their addresses.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `include` | text, default `""` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 locations rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Locations · `itemCount` number — Locations in this answer · `recordCount` number — Locations matching the filter (all pages) · `hasMore` boolean — More rows are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listLocations","version":2,"label":"List locations","config":{"include":"","sort":"","pageSize":30,"returnAll":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listLocations@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `include` | text, default `""` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 locations rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"include":"","sort":"","pageSize":30,"returnAll":true,"connection":"<connection id>"}
{"include":"","sort":"","pageSize":30,"returnAll":true,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listNotes@2` — Teamtailor — List notes

Lists Teamtailor notes and reviews.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `include` | text, default `"candidate,user"` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 notes rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Notes · `itemCount` number — Notes in this answer · `recordCount` number — Notes matching the filter (all pages) · `hasMore` boolean — More rows are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listNotes","version":2,"label":"List notes","config":{"include":"candidate,user","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listNotes@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `include` | text, default `"candidate,user"` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 notes rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"include":"candidate,user","sort":"","pageSize":30,"returnAll":false,"connection":"<connection id>"}
{"include":"candidate,user","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listPartnerResults@2` — Teamtailor — List partner results

Lists assessment and test results Teamtailor partners wrote back.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `candidateId` | text, default `""` | Candidate id: Only the results for one candidate. |
| `include` | text, default `"candidate"` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 results rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Results · `itemCount` number — Results in this answer · `recordCount` number — Results matching the filter (all pages) · `hasMore` boolean — More rows are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listPartnerResults","version":2,"label":"List partner results","config":{"candidateId":"","include":"candidate","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listPartnerResults@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `candidateId` | text, default `""` | Candidate id: Only the results for one candidate. |
| `include` | text, default `"candidate"` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 results rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"candidateId":"","include":"candidate","sort":"","pageSize":30,"returnAll":false,"connection":"<connection id>"}
{"candidateId":"","include":"candidate","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listRejectReasons@2` — Teamtailor — List reject reasons

Lists the reject reasons configured in Teamtailor.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 reasons rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Reject reasons · `itemCount` number — Reject reasons in this answer · `recordCount` number — Reject reasons matching the filter (all pages) · `hasMore` boolean — More rows are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listRejectReasons","version":2,"label":"List reject reasons","config":{"sort":"","pageSize":30,"returnAll":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listRejectReasons@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 reasons rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"sort":"","pageSize":30,"returnAll":true,"connection":"<connection id>"}
{"sort":"","pageSize":30,"returnAll":true,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listStages@2` — Teamtailor — List stages

Lists the stages of a Teamtailor job's pipeline, in order.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `jobId` | text, default `""` | Job id: Fill this in: Teamtailor only sorts stages by their board order when the list is narrowed to one job. |
| `stageType` | "" or "Inbox" or "In process" or "Hired", default `""` | Stage type |
| `include` | text, default `""` | Also fetch |
| `sort` | text, default `"row-order"` | Sort by: row-order is the board's own order, and only sorts right inside one job. |
| `pageSize` | number, default `30` | Stages per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 stages rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Stages · `itemCount` number — Stages in this answer · `recordCount` number — Stages matching the filter (all pages) · `hasMore` boolean — More stages are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listStages","version":2,"label":"List stages","config":{"jobId":"","stageType":"","include":"","sort":"row-order","pageSize":30,"returnAll":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listStages@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `jobId` | text, default `""` | Job id: Fill this in: Teamtailor only sorts stages by their board order when the list is narrowed to one job. |
| `stageType` | "" or "Inbox" or "In process" or "Hired", default `""` | Stage type |
| `include` | text, default `""` | Also fetch |
| `sort` | text, default `"row-order"` | Sort by: row-order is the board's own order, and only sorts right inside one job. |
| `pageSize` | number, default `30` | Stages per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `true` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 stages rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"jobId":"","stageType":"","include":"","sort":"row-order","pageSize":30,"returnAll":true,"connection":"<connection id>"}
{"jobId":"","stageType":"","include":"","sort":"row-order","pageSize":30,"returnAll":true,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listUploads@2` — Teamtailor — List candidate files

Lists the files attached to Teamtailor candidates.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `jobId` | text, default `""` | Job id: Only the files attached through one job. |
| `include` | text, default `"candidate"` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 files rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — Files · `itemCount` number — Files in this answer · `recordCount` number — Files matching the filter (all pages) · `hasMore` boolean — More rows are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listUploads","version":2,"label":"List candidate files","config":{"jobId":"","include":"candidate","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listUploads@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `jobId` | text, default `""` | Job id: Only the files attached through one job. |
| `include` | text, default `"candidate"` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 files rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"jobId":"","include":"candidate","sort":"","pageSize":30,"returnAll":false,"connection":"<connection id>"}
{"jobId":"","include":"candidate","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.listUsers@2` — Teamtailor — List users

Lists the recruiters and hiring managers in Teamtailor.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `email` | text, default `""` | E-mail: The exact-match lookup for one colleague. |
| `department` | text, default `""` | Department id — a value from the vendor's own list |
| `externalId` | text, default `""` | External id: The id your own HR system knows this person by. |
| `include` | text, default `""` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 people rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `items` array — People · `itemCount` number — People in this answer · `recordCount` number — People matching the filter (all pages) · `hasMore` boolean — More rows are waiting behind this page · `nextUrl` string — Teamtailor's own next-page address · `raw` object — The untouched JSON:API answer · `nextCursor` string — Cursor for the next page.

```json
{"type":"teamtailor.listUsers","version":2,"label":"List users","config":{"email":"","department":"","externalId":"","include":"","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `teamtailor.listUsers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `email` | text, default `""` | E-mail: The exact-match lookup for one colleague. |
| `department` | text, default `""` | Department id — a value from the vendor's own list |
| `externalId` | text, default `""` | External id: The id your own HR system knows this person by. |
| `include` | text, default `""` | Also fetch: Related records to fetch with each row, comma separated. They arrive folded into the row. |
| `sort` | text, default `""` | Sort by: A minus in front turns the order around. |
| `pageSize` | number, default `30` | Rows per call: 1 to 30 — Teamtailor's own maximum is 30. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Teamtailor's own next-page link, and refuses past 5,000 people rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"email":"","department":"","externalId":"","include":"","sort":"","pageSize":30,"returnAll":false,"connection":"<connection id>"}
{"email":"","department":"","externalId":"","include":"","sort":"","pageSize":30,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `teamtailor.moveJobApplication@1` — Teamtailor — Move application to stage

Moves a Teamtailor application to another stage, or rejects it.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `jobApplicationId` | text, required | Application id: The List job applications node gives you the id. |
| `stageId` | text, default `""` | Stage id: The List stages node, filtered on this job, gives you the id. A stage belongs to one job — a stage id from another job is refused. |
| `reject` | true/false, default `false` | Reject the application: A Teamtailor rejection can send the candidate an automated e-mail, depending on the job's triggers. |
| `rejectReasonId` | text, default `""` | Reject reason id: The List reject reasons node gives you the ids. — used when `reject` is true |
| `rejectedAt` | text, default `""` | Rejected at: Leave empty to use the moment this step runs. — used when `reject` is true |

Output: `jobApplicationId` string — Application id · `jobApplication` object — Application · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.moveJobApplication","version":1,"label":"Move application to stage","config":{"stageId":"","reject":false,"rejectReasonId":"","rejectedAt":"","connection":"<connection id>","jobApplicationId":"<application id>"}}
```

## `teamtailor.setCustomFieldValue@1` — Teamtailor — Set custom field value

Writes a Teamtailor custom field value on a candidate or job.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `customFieldId` | text, required | Custom field id: The List custom fields node gives you the id. |
| `ownerId` | text, required | Owner id: The candidate or job the value belongs to. |
| `ownerType` | "candidates" or "jobs", default `"candidates"` | Belongs to |
| `value` | text, default `""` | Value: Dates are ISO 8601 (2026-09-22T09:00:00+02:00); a multi-select takes a JSON array of option ids. |

Output: `customFieldValueId` string — Value id · `customFieldValue` object — Value · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.setCustomFieldValue","version":1,"label":"Set custom field value","config":{"ownerType":"candidates","value":"","connection":"<connection id>","customFieldId":"<custom field id>","ownerId":"<owner id>"}}
```

## `teamtailor.updateCandidate@1` — Teamtailor — Update candidate

Changes a Teamtailor candidate's details or relations.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `candidateId` | text, required | Candidate id: The List candidates node gives you the id. |
| `email` | text, default `""` | E-mail: Leave empty to keep the address Teamtailor has. |
| `firstName` | text, default `""` | First name |
| `lastName` | text, default `""` | Last name |
| `phone` | text, default `""` | Phone |
| `pitch` | text, default `""` | Pitch |
| `resumeUrl` | text, default `""` | CV address: A publicly reachable address Teamtailor downloads and hosts. |
| `linkedinUrl` | text, default `""` | LinkedIn address |
| `tags` | text, default `""` | Tags: Filling this in REPLACES the tags the candidate has. |
| `unsubscribed` | "" or "true" or "false", default `""` | Unsubscribed from mailings |
| `departmentId` | text, default `""` | Department id: Empty leaves it as it is; the word null clears it. — a value from the vendor's own list |
| `roleId` | text, default `""` | Role id: Empty leaves it as it is; the word null clears it. |
| `divisionId` | text, default `""` | Division id: Empty leaves it as it is; the word null clears it. |
| `locationIds` | text, default `""` | Location ids |
| `regionIds` | text, default `""` | Region ids |
| `attributes` | object (any keys), default `{}` | Anything else: Any other Teamtailor candidate attribute, spelled the way Teamtailor spells it — kebab-case, for example linkedin-uid. |

Output: `candidateId` string — Candidate id · `candidate` object — Candidate · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.updateCandidate","version":1,"label":"Update candidate","config":{"email":"","firstName":"","lastName":"","phone":"","pitch":"","resumeUrl":"","linkedinUrl":"","tags":"","unsubscribed":"","departmentId":"","roleId":"","divisionId":"","locationIds":"","regionIds":"","attributes":{},"connection":"<connection id>","candidateId":"<candidate id>"}}
```

## `teamtailor.updateCustomFieldValue@1` — Teamtailor — Update custom field value

Changes a Teamtailor custom field value you already wrote.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `customFieldValueId` | text, required | Value id: The List custom field values node gives you the id. |
| `value` | text, default `""` | Value: Dates are ISO 8601 (2026-09-22T09:00:00+02:00); a multi-select takes a JSON array of option ids. |

Output: `customFieldValueId` string — Value id · `customFieldValue` object — Value · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.updateCustomFieldValue","version":1,"label":"Update custom field value","config":{"value":"","connection":"<connection id>","customFieldValueId":"<value id>"}}
```

## `teamtailor.updateJob@1` — Teamtailor — Update job

Changes a Teamtailor job, including publishing or archiving it.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `jobId` | text, required | Job id: The List jobs node gives you the id. |
| `status` | "" or "draft" or "published" or "unlisted" or "archived" or "scheduled", default `""` | Status: This is how a Teamtailor job is published, unlisted or archived. |
| `title` | text, default `""` | Title: Leave empty to keep the title it has. |
| `body` | text, default `""` | Description |
| `pitch` | text, default `""` | Pitch |
| `remoteStatus` | "" or "none" or "temporary" or "hybrid" or "fully", default `""` | Remote |
| `tags` | text, default `""` | Tags |
| `userId` | text, default `""` | Recruiter id: Moves the job to another person. Leave empty to keep the one it has. |
| `departmentId` | text, default `""` | Department id — a value from the vendor's own list |
| `locationIds` | text, default `""` | Location ids |

Output: `jobId` string — Job id · `job` object — Job · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.updateJob","version":1,"label":"Update job","config":{"status":"","title":"","body":"","pitch":"","remoteStatus":"","tags":"","userId":"","departmentId":"","locationIds":"","connection":"<connection id>","jobId":"<job id>"}}
```

## `teamtailor.uploadCandidateFile@1` — Teamtailor — Upload candidate file

Attaches a CV or document to a Teamtailor candidate.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `teamtailor`) |
| `candidateId` | text, required | Candidate id: The candidate the file belongs to. |
| `fileName` | text, required | File name: The name Teamtailor stores the file under, extension included. — a file name, listed in `requires.files` |
| `source` | "bytes" or "url", default `"bytes"` | The file comes from |
| `contentBase64` | text, default `""` | File contents: Base64 of the file — {{ file("cv.pdf").base64 }}. — its `{{ }}` must give a string; used when `source` is "bytes" |
| `sourceUrl` | text, default `""` | File address: Teamtailor fetches this address itself, so it must be reachable from the public internet. — used when `source` is "url" |

Output: `uploadId` string — Upload id · `upload` object — Upload · `raw` object — The untouched JSON:API answer.

```json
{"type":"teamtailor.uploadCandidateFile","version":1,"label":"Upload candidate file","config":{"source":"bytes","contentBase64":"","sourceUrl":"","connection":"<connection id>","candidateId":"<candidate id>","fileName":"<file name>"}}
```


---

## File: references/nodes/topdesk.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# TOPdesk nodes (`topdesk.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `topdesk.addIncidentAction@1` — TOPdesk — Add note to call

Adds a note to a TOPdesk call's progress trail, visible or hidden from the caller.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `identifyBy` | "number" or "id", default `"number"` | Identify by |
| `incident` | text, required | Call number or id |
| `action` | text, required | Note |
| `invisibleForCaller` | true/false, default `true` | Hidden from the caller: On keeps the note internal; off shows it on the caller's portal. |

Output: `incident` object — Call · `incident.id` string — Id.

```json
{"type":"topdesk.addIncidentAction","version":1,"label":"Add note to call","config":{"identifyBy":"number","invisibleForCaller":true,"connection":"<connection id>","incident":"<call number or id>","action":"<note>"}}
```

## `topdesk.archiveIncident@1` — TOPdesk — Archive call

Archives a TOPdesk call.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `incidentId` | text, required | Call id: The call's TOPdesk id (archiving has no by-number form). |
| `unarchive` | true/false, default `false` | Restore instead: On takes the call out of the archive instead of putting it in. |

Output: `done` boolean — Done.

```json
{"type":"topdesk.archiveIncident","version":1,"label":"Archive call","config":{"unarchive":false,"connection":"<connection id>","incidentId":"<call id>"}}
```

## `topdesk.createIncident@1` — TOPdesk — Create call

Creates a call in TOPdesk for a caller you name.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `callerBy` | "email" or "id" or "unregistered", default `"email"` | Caller |
| `callerEmail` | text, default `""` | Caller e-mail — used when `callerBy` is "email" |
| `callerId` | text, default `""` | Caller id — used when `callerBy` is "id" |
| `callerName` | text, default `""` | Caller name — used when `callerBy` is "unregistered" |
| `briefDescription` | text, default `""` | Brief description: At most 80 characters — TOPdesk refuses a longer one. |
| `request` | text, default `""` | Request: TOPdesk accepts only <i> <em> <b> <strong> <u> <br> <h5> <h6> and inline base64 <img> up to 450×450. |
| `status` | "firstLine" or "secondLine", default `"firstLine"` | Status |
| `category` | text, default `""` | Category: The value's name as it appears in TOPdesk — the List drop-down values node gives you the exact spelling. |
| `subcategory` | text, default `""` | Subcategory: The value's name as it appears in TOPdesk — the List drop-down values node gives you the exact spelling. |
| `callType` | text, default `""` | Call type: The value's name as it appears in TOPdesk — the List drop-down values node gives you the exact spelling. |
| `entryType` | text, default `""` | Entry type: The value's name as it appears in TOPdesk — the List drop-down values node gives you the exact spelling. |
| `impact` | text, default `""` | Impact: The value's name as it appears in TOPdesk — the List drop-down values node gives you the exact spelling. |
| `urgency` | text, default `""` | Urgency: The value's name as it appears in TOPdesk — the List drop-down values node gives you the exact spelling. |
| `priority` | text, default `""` | Priority: The value's name as it appears in TOPdesk — the List drop-down values node gives you the exact spelling. |
| `operator` | text, default `""` | Operator: The value's name as it appears in TOPdesk — the List drop-down values node gives you the exact spelling. |
| `operatorGroup` | text, default `""` | Operator group: The value's name as it appears in TOPdesk — the List drop-down values node gives you the exact spelling. |
| `externalNumber` | text, default `""` | Your reference: TOPdesk does not check it for duplicates, so it cannot stop a call being created twice. Search for it before a create if that matters. |
| `optionalFields` | object of text, default `{}` | Optional fields |

Output: `incidentId` string — Id · `number` string — Call number · `incident` object — Call · `status` string — Status.

```json
{"type":"topdesk.createIncident","version":1,"label":"Create call","config":{"callerBy":"email","callerEmail":"","callerId":"","callerName":"","briefDescription":"","request":"","status":"firstLine","category":"","subcategory":"","callType":"","entryType":"","impact":"","urgency":"","priority":"","operator":"","operatorGroup":"","externalNumber":"","optionalFields":{},"connection":"<connection id>"}}
```

## `topdesk.downloadAttachment@1` — TOPdesk — Download attachment

Downloads a file from a TOPdesk call as base64.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `incidentId` | text, required | Call id |
| `attachmentId` | text, required | Attachment id: The attachment's id — the List attachments node gives you attachments[].id. |

Output: `name` string — File name · `contentType` string — Content type · `base64` string — File contents (base64) · `size` number — Size in bytes.

```json
{"type":"topdesk.downloadAttachment","version":1,"label":"Download attachment","config":{"connection":"<connection id>","incidentId":"<call id>","attachmentId":"<attachment id>"}}
```

## `topdesk.escalateIncident@1` — TOPdesk — Escalate call

Escalates a TOPdesk call one level, with a reason.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `incidentId` | text, required | Call id: The call's TOPdesk id (escalation has no by-number form). |
| `reason` | text, default `""` | Reason |
| `deescalate` | true/false, default `false` | De-escalate: On lowers the call one level instead of raising it. |

Output: `done` boolean — Done.

```json
{"type":"topdesk.escalateIncident","version":1,"label":"Escalate call","config":{"reason":"","deescalate":false,"connection":"<connection id>","incidentId":"<call id>"}}
```

## `topdesk.findCaller@1` — TOPdesk — Find caller

Looks up a TOPdesk caller by name or e-mail, to use on a call.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `callerId` | text, default `""` | Caller id: Look one caller up by their TOPdesk id. |
| `query` | text, default `""` | Filter: TOPdesk's own FIQL filter, e.g. dynamicName=="Adams" or email=gt= — used when `callerId` is "" |

Output: `callers` array — Callers · `callerCount` number — Callers in this answer.

```json
{"type":"topdesk.findCaller","version":1,"label":"Find caller","config":{"callerId":"","query":"","connection":"<connection id>"}}
```

## `topdesk.getIncident@1` — TOPdesk — Get call

Reads one TOPdesk call by its number or its id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `identifyBy` | "number" or "id", default `"number"` | Identify by: A person has the call number on screen; a flow usually has the id. |
| `incident` | text, required | Call number or id |

Output: `incident` object — Call · `incident.id` string — Id · `incident.number` string — Call number · `incident.briefDescription` string — Brief description.

```json
{"type":"topdesk.getIncident","version":1,"label":"Get call","config":{"identifyBy":"number","connection":"<connection id>","incident":"<call number or id>"}}
```

## `topdesk.getProgressTrail@1` — TOPdesk — Get progress trail

Reads the full history of a TOPdesk call — notes, requests and changes.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `incidentId` | text, required | Call id: The call's TOPdesk id (the trail has no by-number form). |

Output: `trail` array — History entries · `trailCount` number — Entries in this answer.

```json
{"type":"topdesk.getProgressTrail","version":1,"label":"Get progress trail","config":{"connection":"<connection id>","incidentId":"<call id>"}}
```

## `topdesk.listAttachments@1` — TOPdesk — List attachments

Lists the files attached to a TOPdesk call.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `incidentId` | text, required | Call id: The call's TOPdesk id. |

Output: `attachments` array — Attachments · `attachmentCount` number — Files in this answer.

```json
{"type":"topdesk.listAttachments","version":1,"label":"List attachments","config":{"connection":"<connection id>","incidentId":"<call id>"}}
```

## `topdesk.listIncidentActions@1` — TOPdesk — List notes

Reads the notes on a TOPdesk call.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `incidentId` | text, required | Call id: The call's TOPdesk id (the actions list has no by-number form). |

Output: `actions` array — Notes · `actionCount` number — Notes in this answer.

```json
{"type":"topdesk.listIncidentActions","version":1,"label":"List notes","config":{"connection":"<connection id>","incidentId":"<call id>"}}
```

## `topdesk.listIncidents@1` — TOPdesk — List calls

Lists TOPdesk calls, filtered with a FIQL query and sorted.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `query` | text, default `""` | Filter: TOPdesk's own FIQL filter, e.g. status==firstLine;caller.dynamicName=="Adams". |
| `sort` | text, default `""` | Sort by: field:asc,field:desc. Sorting on callDate, creationDate, modificationDate, targetDate, closedDate or id is the fast path. |
| `fields` | text, default `""` | Fields to fetch: By default TOPdesk returns every field, which is slow. Name the ones you need. |
| `pageSize` | number, default `200` | Calls per call: 1 to 10,000. TOPdesk's own default is 10 — this one is 200 so a list is not silently short. |
| `returnAll` | true/false, default `false` | Fetch every page: Pages with pageStart until a short page, and refuses past 10,000 calls. Set a sort first — TOPdesk gives no order guarantee across pages. |

Output: `incidents` array — Calls · `incidentCount` number — Calls in this answer.

```json
{"type":"topdesk.listIncidents","version":1,"label":"List calls","config":{"query":"","sort":"","fields":"","pageSize":200,"returnAll":false,"connection":"<connection id>"}}
```

## `topdesk.listSelectionValues@1` — TOPdesk — List drop-down values

Reads the values of one TOPdesk drop-down — categories, call types, statuses and more.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `list` | "call_types" or "entry_types" or "categories" or "subcategories" or "closure_codes" or "durations" or "impacts" or "urgencies" or "priorities" or "statuses" or "slas" or "deescalation-reasons" or "escalation-reasons", default `"categories"` | Drop-down: The exact spelling a create or update node expects when it names a value. |

Output: `values` array — Values · `valueCount` number — Values in this answer.

```json
{"type":"topdesk.listSelectionValues","version":1,"label":"List drop-down values","config":{"list":"categories","connection":"<connection id>"}}
```

## `topdesk.registerTimeSpent@1` — TOPdesk — Register time

Records time spent on a TOPdesk call.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `incidentId` | text, required | Call id: The call's TOPdesk id (time registration has no by-number form). |
| `durationMinutes` | number, required | Minutes |
| `description` | text, default `""` | What was done |
| `operator` | text, default `""` | Operator |

Output: `timeSpent` object — Time registration · `timeSpent.id` string — Id.

```json
{"type":"topdesk.registerTimeSpent","version":1,"label":"Register time","config":{"description":"","operator":"","connection":"<connection id>","incidentId":"<call id>","durationMinutes":1}}
```

## `topdesk.updateIncident@1` — TOPdesk — Update call

Changes a TOPdesk call's category, operator, status or priority.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `identifyBy` | "number" or "id", default `"number"` | Identify by |
| `incident` | text, required | Call number or id |
| `status` | "" or "firstLine" or "secondLine", default `""` | Status |
| `category` | text, default `""` | Category |
| `subcategory` | text, default `""` | Subcategory |
| `operator` | text, default `""` | Operator |
| `operatorGroup` | text, default `""` | Operator group |
| `priority` | text, default `""` | Priority |
| `processingStatus` | text, default `""` | Processing status |

Output: `incident` object — Call · `incident.id` string — Id · `incident.status` string — Status.

```json
{"type":"topdesk.updateIncident","version":1,"label":"Update call","config":{"identifyBy":"number","status":"","category":"","subcategory":"","operator":"","operatorGroup":"","priority":"","processingStatus":"","connection":"<connection id>","incident":"<call number or id>"}}
```

## `topdesk.uploadAttachment@1` — TOPdesk — Attach file to call

Attaches a file to a TOPdesk call.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `topdesk`) |
| `incidentId` | text, required | Call id: The call's TOPdesk id (attachments have no by-number form). |
| `name` | text, required | File name: Pick the file, then paste its base64 below. — a file name, listed in `requires.files` |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("name").base64 }}. Multipart uploads over the "Use fixed IP address" lane are unverified; leave that toggle off for this node. — its `{{ }}` must give a string |
| `invisibleForCaller` | true/false, default `true` | Hidden from the caller: On keeps the attachment internal. |
| `description` | text, default `""` | Description |

Output: `attachment` object — Attachment · `attachment.id` string — Id · `attachment.fileName` string — File name.

```json
{"type":"topdesk.uploadAttachment","version":1,"label":"Attach file to call","config":{"invisibleForCaller":true,"description":"","connection":"<connection id>","incidentId":"<call id>","name":"<file name>","contentBase64":"<file contents>"}}
```


---

## File: references/nodes/twinfield.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Twinfield nodes (`twinfield.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `twinfield.browse@1` — Twinfield — Browse

Pulls Twinfield figures out as rows — a trial balance, open items or ledger detail.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `browseCode` | text, required | Report code: Which Twinfield report to run. 040_1, 050_1 and 060 also need a regime column. Read browse definition lists them all. |
| `columns` | text, default `""` | Columns: Which fields to ask for, and how to narrow them. Twinfield's own advice: ask per period, and ask only for the columns you need — for example [{"field":"fin.trs.head.code"},{"field":"fin.trs.head.number"},{"field":"fin.trs.line.value"},{"field":"fin.trs.head.yearperiod","operator":"between","from":"2026/01","to":"2026/12"}] |
| `sortField` | text, default `""` | Sort on: One of the field names above. |
| `sortOrder` | "ascending" or "descending", default `"ascending"` | Direction |

Output: `rows` array — Rows · `columns` array — Column names · `rowCount` number — Rows in this answer.

```json
{"type":"twinfield.browse","version":1,"label":"Browse","config":{"office":"","columns":"","sortField":"","sortOrder":"ascending","connection":"<connection id>","browseCode":"<report code>"}}
```

## `twinfield.createSalesInvoice@1` — Twinfield — Create sales invoice

Makes a Twinfield sales invoice from articles and lets Twinfield number it.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `invoiceType` | text, required | Invoice type: The Twinfield sales-invoice type this invoice is made in. |
| `customer` | text, required | Customer code: The customer's own Twinfield code — Find dimensions resolves one from a name. |
| `invoiceDate` | text, default `""` | Invoice date: Twinfield writes dates as yyyymmdd. |
| `dueDate` | text, default `""` | Due date |
| `bank` | text, default `""` | Bank |
| `period` | text, default `""` | Period |
| `currency` | text, default `""` | Currency |
| `status` | text, default `""` | Status: Write concept to leave the invoice unfinished in Twinfield. |
| `paymentMethod` | text, default `""` | Payment method |
| `mode` | "post" or "price", default `"post"` | What this run does: Priced only, Twinfield works out the totals and posts nothing — so a flow can show them before anyone is billed. |
| `raiseWarning` | true/false, default `false` | Report Twinfield's warnings |
| `lines` | list of {article?: text, subArticle?: text, quantity?: text, unitsPriceExcl?: text, vatCode?: text, description?: text}, default `[]` | Lines: One row per article. Twinfield prices the invoice from the article and the quantity. |
| `linesJson` | text, default `""` | Lines (JSON): Lines an earlier node built, instead of the rows above — for example [{"article":"1","quantity":"2","unitspriceexcl":"50.00","vatcode":"VH","description":"Consultancy"}] |

Output: `invoice` object — Invoice · `invoiceNumber` string — Invoice number · `valueExcl` string — Total excl. VAT · `valueInc` string — Total incl. VAT.

```json
{"type":"twinfield.createSalesInvoice","version":1,"label":"Create sales invoice","config":{"office":"","invoiceDate":"","dueDate":"","bank":"","period":"","currency":"","status":"","paymentMethod":"","mode":"post","raiseWarning":false,"lines":[],"linesJson":"","connection":"<connection id>","invoiceType":"<invoice type>","customer":"<customer code>"}}
```

## `twinfield.deleteDimension@1` — Twinfield — Delete dimension

Hides a Twinfield customer, supplier, account, cost centre or project.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `dimtype` | "DEB" or "CRD" or "BAS" or "PNL" or "KPL" or "PRJ", default `"DEB"` | Kind |
| `code` | text, required | Code: Twinfield hides the record rather than erasing it; the Create or update node brings it back. |

Output: `dimension` object — Dimension · `code` string — Code · `status` string — Status.

```json
{"type":"twinfield.deleteDimension","version":1,"label":"Delete dimension","config":{"office":"","dimtype":"DEB","connection":"<connection id>","code":"<code>"}}
```

## `twinfield.find@2` — Twinfield — Find

Looks up Twinfield master data — articles, VAT codes, periods, users and more.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `type` | text, default `"ART"` | What to look up: The Twinfield type code. Use Find dimensions for customers and suppliers. |
| `pattern` | text, default `"*"` | Search for: A * stands for any run of characters, so *btw* finds every name holding btw. |
| `field` | "0" or "1" or "2", default `"0"` | Search in |
| `options` | object of text, default `{}` | Extra settings: The type's own narrowing settings — vattype is purchase or sales, dimtype is DEB, CRD, BAS, PNL, KPL or PRJ. |
| `firstRow` | number, default `1` | First row: Where the page starts; the first row is 1. |
| `maxRows` | number, default `100` | Rows per call: How many rows one Finder call asks for. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows the Finder's row counter, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `rows` array — Matches · `columns` array — Column names · `rowCount` number — Matches in this answer · `totalRows` number — Matches Twinfield counted · `hasMore` boolean — More matches are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"twinfield.find","version":2,"label":"Find","config":{"office":"","type":"ART","pattern":"*","field":"0","options":{},"firstRow":1,"maxRows":100,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `twinfield.find@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `type` | text, default `"ART"` | What to look up: The Twinfield type code. Use Find dimensions for customers and suppliers. |
| `pattern` | text, default `"*"` | Search for: A * stands for any run of characters, so *btw* finds every name holding btw. |
| `field` | "0" or "1" or "2", default `"0"` | Search in |
| `options` | object of text, default `{}` | Extra settings: The type's own narrowing settings — vattype is purchase or sales, dimtype is DEB, CRD, BAS, PNL, KPL or PRJ. |
| `firstRow` | number, default `1` | First row: Where the page starts; the first row is 1. |
| `maxRows` | number, default `100` | Rows per call: How many rows one Finder call asks for. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows the Finder's row counter, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"office":"","type":"ART","pattern":"*","field":"0","options":{},"firstRow":1,"maxRows":100,"returnAll":false,"connection":"<connection id>"}
{"office":"","type":"ART","pattern":"*","field":"0","options":{},"firstRow":1,"maxRows":100,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `twinfield.findDimensions@2` — Twinfield — Find dimensions

Finds Twinfield customers, suppliers or accounts by name or code.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `dimtype` | "DEB" or "CRD" or "BAS" or "PNL" or "KPL" or "PRJ", default `"DEB"` | Kind |
| `pattern` | text, default `"*"` | Search for: A * stands for any run of characters, so *acme* finds every name holding acme. |
| `field` | "0" or "1" or "2", default `"0"` | Search in: Which column the pattern is matched against. |
| `level` | text, default `""` | Level: Restricts the search to one dimension level; leave empty for all of them. |
| `firstRow` | number, default `1` | First row: Where the page starts; the first row is 1. |
| `maxRows` | number, default `100` | Rows per call: How many rows one Finder call asks for. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows the Finder's row counter, and refuses past 5,000 rows rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `rows` array — Matches · `columns` array — Column names · `rowCount` number — Matches in this answer · `totalRows` number — Matches Twinfield counted · `hasMore` boolean — More matches are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"twinfield.findDimensions","version":2,"label":"Find dimensions","config":{"office":"","dimtype":"DEB","pattern":"*","field":"0","level":"","firstRow":1,"maxRows":100,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `twinfield.findDimensions@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `dimtype` | "DEB" or "CRD" or "BAS" or "PNL" or "KPL" or "PRJ", default `"DEB"` | Kind |
| `pattern` | text, default `"*"` | Search for: A * stands for any run of characters, so *acme* finds every name holding acme. |
| `field` | "0" or "1" or "2", default `"0"` | Search in: Which column the pattern is matched against. |
| `level` | text, default `""` | Level: Restricts the search to one dimension level; leave empty for all of them. |
| `firstRow` | number, default `1` | First row: Where the page starts; the first row is 1. |
| `maxRows` | number, default `100` | Rows per call: How many rows one Finder call asks for. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows the Finder's row counter, and refuses past 5,000 rows rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"office":"","dimtype":"DEB","pattern":"*","field":"0","level":"","firstRow":1,"maxRows":100,"returnAll":false,"connection":"<connection id>"}
{"office":"","dimtype":"DEB","pattern":"*","field":"0","level":"","firstRow":1,"maxRows":100,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `twinfield.listOffices@1` — Twinfield — List offices

Lists the Twinfield companies this login can act in.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection: The company codes this node answers are what every other Twinfield node needs. — a connection id from `requires.connections` (provider `twinfield`) |

Output: `offices` array — Companies · `officeCount` number — Companies in this answer.

```json
{"type":"twinfield.listOffices","version":1,"label":"List offices","config":{"connection":"<connection id>"}}
```

## `twinfield.postTransaction@1` — Twinfield — Post transaction

Posts a sales, purchase, journal, cash or bank transaction in Twinfield.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `transactionCode` | text, required | Day book: The Twinfield transaction type — VRK for sales, INK for purchase, MEMO for a journal entry. |
| `transactionNumber` | text, default `""` | Transaction number: Fill this in to CHANGE a provisional transaction. Leave it empty and Twinfield assigns a new number, which means a second run posts a second transaction. |
| `destiny` | "temporary" or "final", default `"temporary"` | Post as: Only a provisional transaction can still be changed or deleted. |
| `date` | text, default `""` | Date: Twinfield writes dates as yyyymmdd. |
| `period` | text, default `""` | Period: The bookkeeping period this entry lands in. |
| `currency` | text, default `""` | Currency |
| `invoiceNumber` | text, default `""` | Invoice number |
| `dueDate` | text, default `""` | Due date |
| `paymentReference` | text, default `""` | Payment reference |
| `freeText1` | text, default `""` | Free text 1 |
| `freeText2` | text, default `""` | Free text 2 |
| `freeText3` | text, default `""` | Free text 3 |
| `autoBalanceVat` | true/false, default `true` | Let Twinfield balance the VAT |
| `raiseWarning` | true/false, default `false` | Report Twinfield's warnings: Off, Twinfield keeps its warnings to itself and posts anyway. |
| `lines` | list of {type?: "total" or "detail" or "vat", dim1?: text, dim2?: text, debitCredit?: "debit" or "credit", value?: text, vatCode?: text, description?: text}, default `[]` | Lines: The debit and credit sides have to balance, unless Twinfield balances the VAT for you. |
| `linesJson` | text, default `""` | Lines (JSON): Lines an earlier node built, instead of the rows above — for example [{"@type":"total","dim1":"1300","debitcredit":"debit","value":"121.00"},{"@type":"detail","dim1":"8000","debitcredit":"credit","value":"100.00","vatcode":"VH"}] |

Output: `transaction` object — Transaction · `transactionNumber` string — Transaction number · `result` string — Twinfield's own result marker.

```json
{"type":"twinfield.postTransaction","version":1,"label":"Post transaction","config":{"office":"","transactionNumber":"","destiny":"temporary","date":"","period":"","currency":"","invoiceNumber":"","dueDate":"","paymentReference":"","freeText1":"","freeText2":"","freeText3":"","autoBalanceVat":true,"raiseWarning":false,"lines":[],"linesJson":"","connection":"<connection id>","transactionCode":"<day book>"}}
```

## `twinfield.processXml@1` — Twinfield — Send XML document

Sends any Twinfield XML document and reads the answer back.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. The offices list is the one document that takes none. |
| `xmlRequest` | text, required | Document: The Twinfield document itself — the sign-in, the company code, the envelope and both of Twinfield's failure channels are handled for you. It starts with <list>, <read>, <dimension>, <transaction>, <salesinvoice> or <columns>, for example <read><type>dimensions</type><office>001</office><dimtype>DEB</dimtype><code>1000</code></read>. A value you bring in with {{ }} goes in exactly as written, so wrap one that can hold & or < yourself. |
| `mode` | "read" or "write", default `"write"` | What this document does: This is what tells the engine whether the call is safe to retry — say reads about a document that posts, and a retry posts twice. |
| `raw` | true/false, default `false` | Answer as text: Hands the answer back as the XML text Twinfield sent, instead of reading it into data. |

Output: `result` string — Twinfield's own result marker · `xml` string — The answer as text · `parsed` any — The answer as data.

```json
{"type":"twinfield.processXml","version":1,"label":"Send XML document","config":{"office":"","mode":"write","raw":false,"connection":"<connection id>","xmlRequest":"<document>"}}
```

## `twinfield.readBrowseDefinition@1` — Twinfield — Read browse definition

Shows which Twinfield reports exist and which columns one of them offers.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `browseCode` | text, default `""` | Report code: Leave empty to list every report this login can run; fill one in for its columns. |

Output: `definitions` array — Reports · `definitionCount` number — Reports in this answer · `columns` array — Columns of the chosen report · `columnCount` number — Columns in this answer.

```json
{"type":"twinfield.readBrowseDefinition","version":1,"label":"Read browse definition","config":{"office":"","browseCode":"","connection":"<connection id>"}}
```

## `twinfield.readDimension@1` — Twinfield — Read dimension

Reads one Twinfield customer, supplier, ledger account, cost centre or project.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. The List offices node shows which codes this login reaches. |
| `dimtype` | "DEB" or "CRD" or "BAS" or "PNL" or "KPL" or "PRJ", default `"DEB"` | Kind: Which master-data book this record lives in. |
| `code` | text, required | Code: The record's own Twinfield code — the Find dimensions node resolves one from a name. |

Output: `dimension` object — Dimension · `code` string — Code · `name` string — Name · `level` number — Level · `inUse` boolean — In use · `status` string — Status.

```json
{"type":"twinfield.readDimension","version":1,"label":"Read dimension","config":{"office":"","dimtype":"DEB","connection":"<connection id>","code":"<code>"}}
```

## `twinfield.readSalesInvoice@1` — Twinfield — Read sales invoice

Reads one Twinfield sales invoice with its lines and totals.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `invoiceType` | text, required | Invoice type: The Twinfield sales-invoice type the invoice was made in. |
| `invoiceNumber` | text, required | Invoice number: The number Twinfield assigned when the invoice was made. |

Output: `invoice` object — Invoice · `invoiceNumber` string — Invoice number · `valueExcl` string — Total excl. VAT · `valueInc` string — Total incl. VAT.

```json
{"type":"twinfield.readSalesInvoice","version":1,"label":"Read sales invoice","config":{"office":"","connection":"<connection id>","invoiceType":"<invoice type>","invoiceNumber":"<invoice number>"}}
```

## `twinfield.readTransaction@1` — Twinfield — Read transaction

Reads one posted Twinfield transaction with all of its lines.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `transactionCode` | text, required | Day book: The Twinfield transaction type the entry was posted in — VRK, INK, MEMO. |
| `transactionNumber` | text, required | Transaction number: The number Twinfield assigned when the entry was posted. |

Output: `transaction` object — Transaction · `transactionNumber` string — Transaction number · `result` string — Twinfield's own result marker.

```json
{"type":"twinfield.readTransaction","version":1,"label":"Read transaction","config":{"office":"","connection":"<connection id>","transactionCode":"<day book>","transactionNumber":"<transaction number>"}}
```

## `twinfield.upsertDimension@1` — Twinfield — Create or update dimension

Creates a Twinfield customer, supplier, account, cost centre or project, or updates it.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `twinfield`) |
| `office` | text, default `""` | Company code: Leave empty to use the company code on the connection. |
| `dimtype` | "DEB" or "CRD" or "BAS" or "PNL" or "KPL" or "PRJ", default `"DEB"` | Kind: Which master-data book this record lives in. |
| `code` | text, required | Code: The record's own Twinfield code. An existing code updates that record; a new one creates it. |
| `name` | text, default `""` | Name |
| `shortName` | text, default `""` | Short name |
| `website` | text, default `""` | Website |
| `financials` | object of text, default `{}` | Financial settings: The <financials> block, one Twinfield element per row. Empty rows are left out. |
| `addresses` | text, default `""` | Addresses (JSON): A list of addresses. A key starting with @ becomes an attribute, so an address carries its id, type and default flag — for example [{"@id":"1","@type":"invoice","@default":"true","name":"Acme","city":"Amsterdam"}] |
| `banks` | text, default `""` | Bank accounts (JSON): A list of bank accounts, written the same way — for example [{"@default":"true","ascription":"Acme","iban":"NL91ABNA0417164300"}] |
| `groups` | text, default `""` | Group: The dimension group this record belongs to. |
| `extraXml` | text, default `""` | Extra XML: Twinfield's dimension document has more branches than this form shows; anything you write here goes in as written, inside <dimension> — for example <creditmanagement><basecreditlimit>5000</basecreditlimit></creditmanagement>. |

Output: `dimension` object — Dimension · `code` string — Code · `name` string — Name · `status` string — Status.

```json
{"type":"twinfield.upsertDimension","version":1,"label":"Create or update dimension","config":{"office":"","dimtype":"DEB","name":"","shortName":"","website":"","financials":{},"addresses":"","banks":"","groups":"","extraXml":"","connection":"<connection id>","code":"<code>"}}
```


---

## File: references/nodes/xml.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# xml nodes (`xml.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `xml.build@1` — XML — Build

Makes XML text out of an object, ready to send or save.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `value` | any JSON, optional | XML value: The parsed document — XML — Parse hands its whole output over. — its `{{ }}` must give an object |

Output: `text` string — XML text.

```json
{"type":"xml.build","version":1,"label":"Build","config":{"value":"<xml value>"}}
```

## `xml.parse@1` — XML — Parse

Turns XML text into data — every element is a list, whether it holds one item or many.

Ports: `out` · changes nothing outside the run

| config key | value | meaning |
|---|---|---|
| `text` | any JSON, optional | XML text: The text of the document — File — Read hands it over with this name. — its `{{ }}` must give a string |

Output: depends on the data.

```json
{"type":"xml.parse","version":1,"label":"Parse","config":{"text":"<xml text>"}}
```


---

## File: references/nodes/zendesk.md

<!-- Generated from the Flowmage node catalogue. Do not edit by hand. -->

# Zendesk nodes (`zendesk.*`)

Each section: what the node does, its output ports, every config key, its output and a node to start from. Write the newest version; an older version below a node is only for upgrading a flow that has it. `<…>` in an example is a value you must replace.

## `zendesk.addComment@1` — Zendesk — Add comment

Adds a public reply or an internal note to a Zendesk ticket.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `ticketId` | text, required | Ticket id |
| `body` | text, required | Comment: Zendesk silently cuts a comment off past 64 kB, and a ticket holds 5,000. |
| `bodyFormat` | "text" or "html", default `"text"` | Comment format |
| `public` | true/false, default `true` | Visible to the customer: Off writes an internal note only agents see, and sends nobody an e-mail. |
| `authorId` | text, default `""` | Author id: The agent the comment is written by. Left empty, it is the user this connection's OAuth client belongs to. |
| `status` | "" or "new" or "open" or "pending" or "hold" or "solved" or "closed", default `""` | Set the status too: Answer and solve in one step — the most common pair. |
| `uploadTokens` | text, default `""` | Attachment tokens: Comma-separated values from the Upload attachment node. One expires 60 minutes after the upload. |
| `emailCcs` | text, default `""` | E-mail CCs: Needs CCs and followers switched on in Zendesk. |

Output: `ticketId` string — Ticket id · `commentId` string — Comment id · `ticket` object — Ticket.

```json
{"type":"zendesk.addComment","version":1,"label":"Add comment","config":{"bodyFormat":"text","public":true,"authorId":"","status":"","uploadTokens":"","emailCcs":"","connection":"<connection id>","ticketId":"<ticket id>","body":"<comment>"}}
```

## `zendesk.createOrUpdateOrganization@1` — Zendesk — Create or update organization

Creates a Zendesk organization, or updates the one that matches.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `name` | text, required | Name: Matched exactly when there is no external id, so a changed name creates a second organization. Zendesk accepts at most five changes a minute for one organization. |
| `externalId` | text, default `""` | Your own id: Matched before the name — the safest way to keep one company one. |
| `domainNames` | text, default `""` | E-mail domains: Comma-separated. Anyone writing in from one of these domains is added to this organization automatically. |
| `details` | text, default `""` | Details |
| `notes` | text, default `""` | Notes: Only agents see this. |
| `groupId` | text, default `""` | Default group id: New tickets from this organization land with this group. |
| `tags` | text, default `""` | Tags: Comma-separated. |
| `organizationFields` | object of text, default `{}` | Custom fields: Keyed by the field's KEY, not by a number — you find it under Admin Center → People → Organization fields. |
| `sharedTickets` | true/false, default `false` | Everyone here sees each other's tickets |
| `sharedComments` | true/false, default `false` | Everyone here may comment on them |

Output: `organizationId` string — Organization id · `organization` object — Organization · `created` boolean — Whether this run created the organization.

```json
{"type":"zendesk.createOrUpdateOrganization","version":1,"label":"Create or update organization","config":{"externalId":"","domainNames":"","details":"","notes":"","groupId":"","tags":"","organizationFields":{},"sharedTickets":false,"sharedComments":false,"connection":"<connection id>","name":"<name>"}}
```

## `zendesk.createOrUpdateUser@1` — Zendesk — Create or update user

Creates a Zendesk user, or updates the one that matches.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `name` | text, required | Name: Zendesk accepts at most five changes a minute for one person, so a loop over many people needs to go slowly. |
| `email` | text, default `""` | E-mail address: What Zendesk matches on when there is no external id. Without either of the two, every run creates another person. |
| `externalId` | text, default `""` | Your own id: Matched before the e-mail address — the safest way to keep one person one. |
| `role` | "" or "end-user" or "agent" or "admin", default `""` | Role |
| `organizationId` | text, default `""` | Organization id: The List organizations node gives you the id. |
| `phone` | text, default `""` | Phone number |
| `tags` | text, default `""` | Tags: Comma-separated. |
| `userFields` | object of text, default `{}` | Custom fields: A user field is keyed by its KEY here, not by a number — the opposite of a ticket field. You find the key in Zendesk under Admin Center → People → Fields. |
| `notes` | text, default `""` | Notes: Only agents see this. |
| `details` | text, default `""` | Details |
| `timeZone` | text, default `""` | Time zone |
| `locale` | text, default `""` | Language |
| `verified` | true/false, default `false` | Treat the e-mail address as verified: On, Zendesk does not send this person a confirmation mail. |

Output: `userId` string — User id · `user` object — User · `created` boolean — Whether this run created the person.

```json
{"type":"zendesk.createOrUpdateUser","version":1,"label":"Create or update user","config":{"email":"","externalId":"","role":"","organizationId":"","phone":"","tags":"","userFields":{},"notes":"","details":"","timeZone":"","locale":"","verified":false,"connection":"<connection id>","name":"<name>"}}
```

## `zendesk.createTicket@1` — Zendesk — Create ticket

Creates a Zendesk ticket with its first comment.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `subject` | text, default `""` | Subject: Leave empty and Zendesk uses the first line of the comment. |
| `body` | text, required | First comment: Zendesk collapses runs of spaces and newlines in plain text, and silently cuts a comment off past 64 kB. |
| `bodyFormat` | "text" or "html", default `"text"` | Comment format |
| `public` | true/false, default `true` | Visible to the customer: Off writes an internal note only agents see. |
| `requesterEmail` | text, default `""` | Requester e-mail: Zendesk creates the end-user if this address is new. |
| `requesterName` | text, default `""` | Requester name — used when `requesterEmail` is not "" |
| `requesterId` | text, default `""` | Requester id: Use instead of the e-mail when you already have the user id. |
| `assigneeId` | text, default `""` | Assignee id |
| `groupId` | text, default `""` | Group id: The List groups node gives you the id. |
| `organizationId` | text, default `""` | Organization id |
| `type` | "" or "problem" or "incident" or "question" or "task", default `""` | Type |
| `priority` | "" or "urgent" or "high" or "normal" or "low", default `""` | Priority |
| `status` | "new" or "open" or "pending" or "hold" or "solved", default `"new"` | Status |
| `tags` | text, default `""` | Tags: Comma-separated. |
| `customFields` | object of text, default `{}` | Custom fields: Zendesk keys a custom field by its numeric id, never by its title — the List ticket fields node gives you the ids and, for drop-downs, the option tags. |
| `externalId` | text, default `""` | External id: Your own id for this ticket. Zendesk does not enforce it as unique, so it does not stop a second run creating a second ticket. |
| `dueAt` | text, default `""` | Due at: Only a ticket of type Task has a due date. |
| `ticketFormId` | text, default `""` | Ticket form id |
| `brandId` | text, default `""` | Brand id |
| `emailCcs` | text, default `""` | E-mail CCs: Needs CCs and followers switched on in Zendesk. |
| `uploadTokens` | text, default `""` | Attachment tokens: Comma-separated values from the Upload attachment node. One expires 60 minutes after the upload. |

Output: `ticketId` string — Ticket id · `ticket` object — Ticket · `ticketUrl` string — Address of the ticket in Zendesk · `requesterId` string — Requester id.

```json
{"type":"zendesk.createTicket","version":1,"label":"Create ticket","config":{"subject":"","bodyFormat":"text","public":true,"requesterEmail":"","requesterName":"","requesterId":"","assigneeId":"","groupId":"","organizationId":"","type":"","priority":"","status":"new","tags":"","customFields":{},"externalId":"","dueAt":"","ticketFormId":"","brandId":"","emailCcs":"","uploadTokens":"","connection":"<connection id>","body":"<first comment>"}}
```

## `zendesk.getTicket@1` — Zendesk — Get ticket

Reads one Zendesk ticket, optionally with its related records.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `ticketId` | text, required | Ticket id |
| `sideloads` | text, default `""` | Also fetch: Comma-separated related records Zendesk returns beside the ticket, under Also fetched. |

Output: `ticket` object — Ticket · `ticketId` string — Ticket id · `ticketUrl` string — Address of the ticket in Zendesk · `sideloaded` object — Also fetched.

```json
{"type":"zendesk.getTicket","version":1,"label":"Get ticket","config":{"sideloads":"","connection":"<connection id>","ticketId":"<ticket id>"}}
```

## `zendesk.getUser@1` — Zendesk — Get user

Reads one Zendesk user by id, e-mail address or your own id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `lookupBy` | "id" or "externalId" or "email", default `"id"` | Look the person up by: Only Zendesk's own id is guaranteed to find one person — the other two can match several, which the answer's count tells you. |
| `value` | text, required | Value |

Output: `user` object — User · `userId` string — User id · `matchCount` number — How many people matched.

```json
{"type":"zendesk.getUser","version":1,"label":"Get user","config":{"lookupBy":"id","connection":"<connection id>","value":"<value>"}}
```

## `zendesk.listChangedTickets@1` — Zendesk — Tickets changed since

Reads every Zendesk ticket created or changed since a moment.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `startTime` | text, default `""` | Changed since: An ISO 8601 moment or the number of seconds since 1970. Keep it at least a minute in the past: Zendesk indexes a change a moment after it happens, and a start time on the second loses the changes still being written. |
| `cursor` | text, default `""` | Continue from: The value the previous run answered. Fill it in and Changed since is ignored — this is how a sync picks up exactly where it left off. |
| `excludeDeleted` | true/false, default `false` | Leave out deleted tickets |
| `sideloads` | text, default `""` | Also fetch: Comma-separated related records. This endpoint does NOT accept last_audits, however much the other ticket reads do. |
| `maxPages` | number, default `10` | Pages per run: Each page holds up to 1,000 tickets, a run stops at 5,000, and Zendesk allows 10 of these calls a minute (30 with the High Volume add-on). When the run stops before the end, use the cursor it answers to continue. The same ticket can come back on two pages, so whatever reads this has to tolerate seeing one twice. |

Output: `tickets` array — Tickets · `ticketCount` number — Tickets in this answer · `nextCursor` string — Continue from this next time · `endOfStream` boolean — Whether Zendesk has nothing more for now · `pages` number — Pages this run read.

```json
{"type":"zendesk.listChangedTickets","version":1,"label":"Tickets changed since","config":{"startTime":"","cursor":"","excludeDeleted":false,"sideloads":"","maxPages":10,"connection":"<connection id>"}}
```

## `zendesk.listComments@2` — Zendesk — List ticket comments

Reads the comments and notes on a Zendesk ticket.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `ticketId` | text, required | Ticket id |
| `sort` | "created_at" or "-created_at", default `"created_at"` | Order |
| `includeUsers` | true/false, default `false` | Also fetch the authors: A comment names its author by id only — this returns the people beside it. |
| `includeInlineImages` | true/false, default `false` | Also fetch images placed in the text |
| `limit` | number, default `100` | Comments per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Zendesk's cursor, and refuses past 5,000 comments rather than answering a short list. A ticket holds at most 5,000. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `comments` array — Comments · `commentCount` number — Comments in this answer · `hasMore` boolean — More comments are waiting behind this page · `nextCursor` string — Cursor for the next page · `users` array — The authors.

```json
{"type":"zendesk.listComments","version":2,"label":"List ticket comments","config":{"sort":"created_at","includeUsers":false,"includeInlineImages":false,"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","ticketId":"<ticket id>"}}
```

### Older version `zendesk.listComments@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `ticketId` | text, required | Ticket id |
| `sort` | "created_at" or "-created_at", default `"created_at"` | Order |
| `includeUsers` | true/false, default `false` | Also fetch the authors: A comment names its author by id only — this returns the people beside it. |
| `includeInlineImages` | true/false, default `false` | Also fetch images placed in the text |
| `limit` | number, default `100` | Comments per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Zendesk's cursor, and refuses past 5,000 comments rather than answering a short list. A ticket holds at most 5,000. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"sort":"created_at","includeUsers":false,"includeInlineImages":false,"limit":100,"returnAll":false,"connection":"<connection id>","ticketId":"<ticket id>"}
{"sort":"created_at","includeUsers":false,"includeInlineImages":false,"limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","ticketId":"<ticket id>"}
```

## `zendesk.listGroups@2` — Zendesk — List groups

Lists Zendesk groups and their ids.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `assignableOnly` | true/false, default `false` | Only the ones a ticket can be given to: Leaves out the groups that exist but take no tickets. |
| `userId` | text, default `""` | Only this person's groups: The Get user node gives you the id. |
| `limit` | number, default `100` | Groups per call: 1 to 100. |
| `returnAll` | true/false, default `true` | Fetch every page: On by default: an account has few groups, and half a list of them is no use. It refuses past 5,000 rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `groups` array — Groups · `groupCount` number — Groups in this answer · `hasMore` boolean — More groups are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"zendesk.listGroups","version":2,"label":"List groups","config":{"assignableOnly":false,"userId":"","limit":100,"returnAll":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `zendesk.listGroups@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `assignableOnly` | true/false, default `false` | Only the ones a ticket can be given to: Leaves out the groups that exist but take no tickets. |
| `userId` | text, default `""` | Only this person's groups: The Get user node gives you the id. |
| `limit` | number, default `100` | Groups per call: 1 to 100. |
| `returnAll` | true/false, default `true` | Fetch every page: On by default: an account has few groups, and half a list of them is no use. It refuses past 5,000 rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"assignableOnly":false,"userId":"","limit":100,"returnAll":true,"connection":"<connection id>"}
{"assignableOnly":false,"userId":"","limit":100,"returnAll":true,"cursor":"","connection":"<connection id>"}
```

## `zendesk.listOrganizations@2` — Zendesk — List organizations

Lists Zendesk organizations, or finds one by name or your own id.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `findBy` | "" or "name" or "externalId", default `""` | Find one by: Both searches match exactly, though upper and lower case do not matter. |
| `value` | text, default `""` | Value — used when `findBy` is not "" |
| `userId` | text, default `""` | Only this person's organizations: A person can belong to more than one organization. — used when `findBy` is "" |
| `limit` | number, default `100` | Organizations per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Zendesk's cursor, and refuses past 5,000 organizations rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `organizations` array — Organizations · `organizationCount` number — Organizations in this answer · `hasMore` boolean — More organizations are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"zendesk.listOrganizations","version":2,"label":"List organizations","config":{"findBy":"","value":"","userId":"","limit":100,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `zendesk.listOrganizations@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `findBy` | "" or "name" or "externalId", default `""` | Find one by: Both searches match exactly, though upper and lower case do not matter. |
| `value` | text, default `""` | Value — used when `findBy` is not "" |
| `userId` | text, default `""` | Only this person's organizations: A person can belong to more than one organization. — used when `findBy` is "" |
| `limit` | number, default `100` | Organizations per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Zendesk's cursor, and refuses past 5,000 organizations rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"findBy":"","value":"","userId":"","limit":100,"returnAll":false,"connection":"<connection id>"}
{"findBy":"","value":"","userId":"","limit":100,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `zendesk.listTicketFields@2` — Zendesk — List ticket fields

Lists Zendesk ticket fields with their ids and drop-down options.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `locale` | text, default `""` | Language: Which language the titles and drop-down labels come back in. Left empty, Zendesk uses the account's own. Every field carries its own type — text, textarea, checkbox, date, integer, decimal, regexp, partialcreditcard, multiselect, tagger (a drop-down), lookup and multi_lookup — beside the ones Zendesk builds in itself. |
| `includeCreator` | true/false, default `false` | Also say who made each field |
| `limit` | number, default `100` | Fields per call: 1 to 100. |
| `returnAll` | true/false, default `true` | Fetch every page: On by default: an account has a few hundred fields at most, and half a list of them is no use. Zendesk allows 300 of these calls a minute (450 with the High Volume add-on, 40 in a sandbox and 20 on a trial). |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `fields` array — Ticket fields · `fieldCount` number — Fields in this answer · `fieldIds` object — Each custom field's id, by its title · `hasMore` boolean — More fields are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"zendesk.listTicketFields","version":2,"label":"List ticket fields","config":{"locale":"","includeCreator":false,"limit":100,"returnAll":true,"cursor":"","connection":"<connection id>"}}
```

### Older version `zendesk.listTicketFields@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `locale` | text, default `""` | Language: Which language the titles and drop-down labels come back in. Left empty, Zendesk uses the account's own. Every field carries its own type — text, textarea, checkbox, date, integer, decimal, regexp, partialcreditcard, multiselect, tagger (a drop-down), lookup and multi_lookup — beside the ones Zendesk builds in itself. |
| `includeCreator` | true/false, default `false` | Also say who made each field |
| `limit` | number, default `100` | Fields per call: 1 to 100. |
| `returnAll` | true/false, default `true` | Fetch every page: On by default: an account has a few hundred fields at most, and half a list of them is no use. Zendesk allows 300 of these calls a minute (450 with the High Volume add-on, 40 in a sandbox and 20 on a trial). |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"locale":"","includeCreator":false,"limit":100,"returnAll":true,"connection":"<connection id>"}
{"locale":"","includeCreator":false,"limit":100,"returnAll":true,"cursor":"","connection":"<connection id>"}
```

## `zendesk.listTickets@2` — Zendesk — List tickets

Lists Zendesk tickets, newest first, page by page.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `externalId` | text, default `""` | Your own id: Only the tickets carrying this external id. |
| `sort` | "-updated_at" or "updated_at" or "-id" or "id" or "-status" or "status", default `"-updated_at"` | Order |
| `startTime` | text, default `""` | Changed since: Only tickets changed since this moment. For a real sync use the Tickets changed since node — it never misses one. |
| `supportTypeScope` | "agent" or "all" or "ai_agent", default `"agent"` | Whose tickets |
| `sideloads` | text, default `""` | Also fetch: Comma-separated related records Zendesk returns beside the tickets. |
| `limit` | number, default `100` | Tickets per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Zendesk's cursor, and refuses past 5,000 tickets rather than answering a short list. Listing tickets is capped at 100 calls a minute, and a separate counter of 50 a minute applies past page 500. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `tickets` array — Tickets · `ticketCount` number — Tickets in this answer · `hasMore` boolean — More tickets are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"zendesk.listTickets","version":2,"label":"List tickets","config":{"externalId":"","sort":"-updated_at","startTime":"","supportTypeScope":"agent","sideloads":"","limit":100,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `zendesk.listTickets@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `externalId` | text, default `""` | Your own id: Only the tickets carrying this external id. |
| `sort` | "-updated_at" or "updated_at" or "-id" or "id" or "-status" or "status", default `"-updated_at"` | Order |
| `startTime` | text, default `""` | Changed since: Only tickets changed since this moment. For a real sync use the Tickets changed since node — it never misses one. |
| `supportTypeScope` | "agent" or "all" or "ai_agent", default `"agent"` | Whose tickets |
| `sideloads` | text, default `""` | Also fetch: Comma-separated related records Zendesk returns beside the tickets. |
| `limit` | number, default `100` | Tickets per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Zendesk's cursor, and refuses past 5,000 tickets rather than answering a short list. Listing tickets is capped at 100 calls a minute, and a separate counter of 50 a minute applies past page 500. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"externalId":"","sort":"-updated_at","startTime":"","supportTypeScope":"agent","sideloads":"","limit":100,"returnAll":false,"connection":"<connection id>"}
{"externalId":"","sort":"-updated_at","startTime":"","supportTypeScope":"agent","sideloads":"","limit":100,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `zendesk.listUsers@2` — Zendesk — List users

Lists the people in Zendesk, by role, group or organization.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `role` | "" or "end-user" or "agent" or "admin", default `""` | Role |
| `permissionSet` | text, default `""` | Custom role id: Only agents with this custom role. Enterprise accounts only. |
| `externalId` | text, default `""` | Your own id: Only the people carrying this external id. |
| `groupId` | text, default `""` | Only this group: The List groups node gives you the id. |
| `organizationId` | text, default `""` | Only this organization: The List organizations node gives you the id. |
| `limit` | number, default `100` | People per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Zendesk's cursor, and refuses past 5,000 people rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `users` array — People · `userCount` number — People in this answer · `hasMore` boolean — More people are waiting behind this page · `nextCursor` string — Cursor for the next page.

```json
{"type":"zendesk.listUsers","version":2,"label":"List users","config":{"role":"","permissionSet":"","externalId":"","groupId":"","organizationId":"","limit":100,"returnAll":false,"cursor":"","connection":"<connection id>"}}
```

### Older version `zendesk.listUsers@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `role` | "" or "end-user" or "agent" or "admin", default `""` | Role |
| `permissionSet` | text, default `""` | Custom role id: Only agents with this custom role. Enterprise accounts only. |
| `externalId` | text, default `""` | Your own id: Only the people carrying this external id. |
| `groupId` | text, default `""` | Only this group: The List groups node gives you the id. |
| `organizationId` | text, default `""` | Only this organization: The List organizations node gives you the id. |
| `limit` | number, default `100` | People per call: 1 to 100. |
| `returnAll` | true/false, default `false` | Fetch every page: Follows Zendesk's cursor, and refuses past 5,000 people rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"role":"","permissionSet":"","externalId":"","groupId":"","organizationId":"","limit":100,"returnAll":false,"connection":"<connection id>"}
{"role":"","permissionSet":"","externalId":"","groupId":"","organizationId":"","limit":100,"returnAll":false,"cursor":"","connection":"<connection id>"}
```

## `zendesk.search@2` — Zendesk — Search

Finds tickets, users, organizations or groups with a Zendesk search query.

Ports: `out` · reads

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `type` | "ticket" or "user" or "organization" or "group", default `"ticket"` | What to search |
| `query` | text, required | Query: Zendesk's own search language. The node adds the kind from the dropdown above, so do not write it here again. |
| `sortBy` | "" or "updated_at" or "created_at" or "priority" or "status" or "ticket_type", default `""` | Order by — used when `returnAll` is false |
| `sortOrder` | "desc" or "asc", default `"desc"` | Direction — used when `returnAll` is false |
| `limit` | number, default `100` | Results per call: 1 to 100. — used when `returnAll` is false |
| `returnAll` | true/false, default `false` | Fetch every page: Off, Zendesk answers at most 1,000 results however wide the query is. On, the node uses Zendesk's export lane instead, which has no such ceiling but always orders by creation date — so the order fields disappear. It refuses past 5,000 results rather than answering a short list. |
| `cursor` | text, default `""` | Cursor: Paste a previous run's Next cursor to read the page after it. |

Output: `results` array — Results · `resultCount` number — Results in this answer · `hasMore` boolean — More results are waiting behind this page · `nextCursor` string — Cursor for the next page · `totalMatches` number — How many records match in total.

```json
{"type":"zendesk.search","version":2,"label":"Search","config":{"type":"ticket","sortBy":"","sortOrder":"desc","limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","query":"<query>"}}
```

### Older version `zendesk.search@1` — still runs; upgrade it to @2

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `type` | "ticket" or "user" or "organization" or "group", default `"ticket"` | What to search |
| `query` | text, required | Query: Zendesk's own search language. The node adds the kind from the dropdown above, so do not write it here again. |
| `sortBy` | "" or "updated_at" or "created_at" or "priority" or "status" or "ticket_type", default `""` | Order by — used when `returnAll` is false |
| `sortOrder` | "desc" or "asc", default `"desc"` | Direction — used when `returnAll` is false |
| `limit` | number, default `100` | Results per call: 1 to 100. — used when `returnAll` is false |
| `returnAll` | true/false, default `false` | Fetch every page: Off, Zendesk answers at most 1,000 results however wide the query is. On, the node uses Zendesk's export lane instead, which has no such ceiling but always orders by creation date — so the order fields disappear. It refuses past 5,000 results rather than answering a short list. |

Upgrade @1 → @2: set `version` to 2 and replace the config — before, then after:
```json
{"type":"ticket","sortBy":"","sortOrder":"desc","limit":100,"returnAll":false,"connection":"<connection id>","query":"<query>"}
{"type":"ticket","sortBy":"","sortOrder":"desc","limit":100,"returnAll":false,"cursor":"","connection":"<connection id>","query":"<query>"}
```

## `zendesk.updateTicket@1` — Zendesk — Update ticket

Changes status, assignee, tags or fields on a Zendesk ticket.

Ports: `out` · writes (safe to repeat)

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `ticketId` | text, required | Ticket id |
| `status` | "" or "new" or "open" or "pending" or "hold" or "solved" or "closed", default `""` | Status: A closed ticket can never be changed again — Zendesk closes it for good. |
| `priority` | "" or "urgent" or "high" or "normal" or "low", default `""` | Priority |
| `type` | "" or "problem" or "incident" or "question" or "task", default `""` | Type |
| `assigneeId` | text, default `""` | Assignee id |
| `groupId` | text, default `""` | Group id |
| `organizationId` | text, default `""` | Organization id |
| `subject` | text, default `""` | Subject |
| `tags` | text, default `""` | Tags: Comma-separated. |
| `tagMode` | "replace" or "add" or "remove", default `"replace"` | What to do with the tags: Replace wipes the tags the ticket already carries. |
| `customFields` | object of text, default `{}` | Custom fields: Keyed by the field's numeric id, never by its title — the List ticket fields node gives you the ids. |
| `externalId` | text, default `""` | External id |
| `dueAt` | text, default `""` | Due at |
| `safeUpdate` | true/false, default `false` | Refuse if the ticket changed: Zendesk refuses the update when the ticket moved since the moment below — use it when a colleague may be working on the same ticket. |
| `updatedStamp` | text, default `""` | The ticket's updated_at when you read it — used when `safeUpdate` is true |

Output: `ticketId` string — Ticket id · `ticket` object — Ticket · `audit` object — What this change recorded.

```json
{"type":"zendesk.updateTicket","version":1,"label":"Update ticket","config":{"status":"","priority":"","type":"","assigneeId":"","groupId":"","organizationId":"","subject":"","tags":"","tagMode":"replace","customFields":{},"externalId":"","dueAt":"","safeUpdate":false,"updatedStamp":"","connection":"<connection id>","ticketId":"<ticket id>"}}
```

## `zendesk.uploadAttachment@1` — Zendesk — Upload attachment

Uploads a file to Zendesk so a comment can carry it.

Ports: `out` · writes

| config key | value | meaning |
|---|---|---|
| `connection` | text, required | Connection — a connection id from `requires.connections` (provider `zendesk`) |
| `name` | text, required | File name: The name Zendesk stores, extension included. Pick the file, then paste its contents below. — a file name, listed in `requires.files` |
| `contentType` | text, default `"application/octet-stream"` | Media type: What kind of file this is. Zendesk stores it as given, and a wrong value breaks preview and playback in the agent's browser. |
| `contentBase64` | text, required | File contents: Base64 of the file — {{ file("name").base64 }}. Zendesk caps an attachment at 50 MB, and this run's own parse limit is usually the tighter one. — its `{{ }}` must give a string |
| `attachmentSet` | text, default `""` | Add to an earlier upload: The value an earlier Upload attachment node answered. Fill it in to put this file in the same set, so one comment carries both. A set can be used for 60 minutes. |

Output: `attachmentSet` string — Paste this into Attachment tokens · `attachment` object — Attachment · `attachmentId` string — Attachment id · `contentUrl` string — Where the file can be downloaded.

```json
{"type":"zendesk.uploadAttachment","version":1,"label":"Upload attachment","config":{"contentType":"application/octet-stream","attachmentSet":"","connection":"<connection id>","name":"<file name>","contentBase64":"<file contents>"}}
```


---

## File: references/subflows.md

# Subflows

One flow calls another like a function: it passes parameters and gets the result back. Each flow is
its own file; the customer imports the called flow (the **child**) first, then the caller.
Any flow can be called; there is nothing to switch on. The customer sees a flow's parameters
in its Settings → Inputs.

## The child

Top-level key in the child's file:

```json
"subflow": { "params": [
  { "name": "email", "type": "text", "description": "Who to look up" },
  { "name": "limit", "type": "number", "default": 10 }
] }
```

- `name`: starts with a letter or `_`, then letters, digits, `_`; up to 64 characters; unique; not
  `true`, `false` or `null`. At most 25 parameters.
- `type`: `text`, `number`, `boolean`, `object` or `list`.
- `default` (optional): a value of that type, never null. A parameter not passed takes its default,
  else null.
- `description` (optional): up to 500 characters.
- The child reads them as `{{ params.email }}`; a name it does not declare is refused. `trigger` is
  null in a called run.
- Its result is the value of its `core.return`, else the output of its step that finished last.
  End the child in a `core.return` so the caller gets a predictable value.

## The caller (`core.callFlow@2`)

```json
{ "id": "lookup", "type": "core.callFlow", "version": 2, "label": "Look up customer",
  "config": { "flowId": "template:flow:look-up-customer", "mode": "wait", "editorRuns": "version",
              "params": { "email": "{{ trigger.body.email }}", "limit": 5 } },
  "position": { "x": 224, "y": 0 } }
```

with, in `requires`:

```json
"flows": [ { "id": "template:flow:look-up-customer", "name": "Look up customer" } ]
```

- `flowId` is literal text, never `{{ }}`. Import finds the child by the exact `name` in
  `requires.flows` among the workspace's flows and swaps in its id; when no flow has that name it
  clears `flowId` and the customer picks the child in the step. In an edit, keep the ids already
  there.
- `params` keys are the child's parameter names. Types are checked exactly: a `number` parameter
  takes `5` or a whole-value `{{ json.qty }}` that gives a number, never `"5"` or `"{{ json.qty }}
  items"`. Leave a key out (or empty) to use the child's default.
- `mode: "wait"` hands the child's result to the next step (`json`, or
  `node['Look up customer']`); `"detach"` starts the child and goes on at once with
  `{ executionId, flowId, queued }`.
- `editorRuns`: `"version"` (default) or `"draft"`, only for runs started in the editor.

## Rules

- A child must be published in the environment the caller runs in (Test or Prod), or the call
  fails with `NO_ACTIVE_VERSION`.
- At most 3 calls deep, and never a circle (A calls B calls A).
- A child that fails fails the call step with `SUBFLOW_FAILED`; wrap the call in a try/catch to
  handle it (loops-and-errors.md).
- Writing both files at once: give the child its final name, use that name in the caller's
  `requires.flows`, and tell the customer to import the child first. If a flow with that name
  already exists, the import names the new one `Name (2)` and the caller finds the old one.

<!-- source-stamp: compile=0bb4f9e964c5 runtime=ebb2f787556b transfer=34bdb9a2d3f8 -->


---

## File: references/tables-and-files.md

# Tables, files, schemas and data formats

## Workspace tables

A table step names its table by `tableId` and declares it in `requires.tables`:

```json
"requires": { "tables": [ { "id": "template:table:leads", "name": "Leads", "columns": [
  { "name": "Email", "key": "email", "type": "text", "required": true, "unique": true },
  { "name": "First name", "key": "first_name", "type": "text" },
  { "name": "Score", "key": "score", "type": "number", "default": 0 },
  { "name": "Signed up", "key": "signed_up", "type": "datetime" },
  { "name": "Raw", "key": "raw", "type": "json" } ] } ] }
```

- Import finds the table by `name` (any case). When the workspace has none, the step offers
  "Create a table" filled in from `columns`, so write `columns` for every table the flow uses.
- Column `type`: `text`, `number`, `boolean`, `datetime`, `json`. `key`: lower case, starts with a
  letter, then letters, digits, `_`; at most 64 characters. Make it the display name in snake case
  ("First name" → `first_name`). Always write `key`.
- A column may also carry a `kind`, which only changes how it is shown: `choice` (text, with
  `options`), `email` (text), `date` (datetime) or `money` (number, with a `currency` code). A table
  step still sends the plain type's value.
- In an edit, keep the table's `id`, `name` and `columns` as they are.
- Rows are kept separately for Test and Prod: a test run never sees production rows.

### Writing values

`values` and filters use column **keys**. A value is stored as it comes, never converted:

| column | write |
|---|---|
| number | `"{{ json.qty }}"` when that is a number, `5`, or `"{{ toNumber(json.qty) }}"` for text such as `"5"` |
| boolean | `true`, or a whole-value `{{ }}` that gives a boolean |
| text | `"{{ json.name }}"`, or `"{{ toString(json.id) }}"` for a number |
| datetime | an ISO time: `"{{ trigger.receivedAt }}"`, `"{{ $macro.date.iso() }}"` |
| json | a whole-value `{{ }}` that gives an object or list: `"{{ trigger.body }}"` |

`"{{ json.qty }} pcs"` or `"5"` in a number column fails the run. A missing value is null, which
every column accepts, unless it is `required`.

### Steps

- `table.insert` `{ tableId, values }` → `{ rowId, rowVersion }`.
- `table.update` `{ tableId, rowId, values }`: only the keys in `values` change.
- `table.delete` `{ tableId, rowId }`; a row already deleted fails.
- `table.select` `{ tableId, filters, sort, search, maxRows, ifMoreRows, cursor }` →
  `{ rows, filteredCount, totalCount, nextCursor? }`. Each row is
  `{ rowId, rowVersion, createdAt, updatedAt, values: { <key>: … } }`: read
  `node['Find lead'].rows[0].values.email`, never `rows[0].email`.
- A filter: `{ "column": "email", "operator": "equals", "value": "{{ trigger.body.email }}" }`.
  Operators are snake case and depend on the column type (the list is on the table node page);
  `is_empty` / `is_not_empty` take no `value`. A null `value` fails: guard it with `?? ""`.
- `ifMoreRows`: `"fail"` (default) fails when more than `maxRows` match; `"first"` returns what fits
  plus `nextCursor` (or loop over every page: loops-and-errors.md).
- No sort gives oldest first. `search` looks in text and datetime columns.

### Insert or update ("upsert")

There is no upsert step. Select by the key column, then branch:

1. `table.select` "Find lead": filter `email` `equals` the incoming email, `maxRows: 1`,
   `ifMoreRows: "first"`.
2. `core.condition` "Lead exists?": `{{ node['Find lead'].filteredCount > 0 }}`.
3. `true` → `table.update` with `"rowId": "{{ node['Find lead'].rows[0].rowId }}"`.
4. `false` → `table.insert`.

## Files

Workspace files are named by `name` (a flat name: `exports/report.csv` is a name, not a folder),
kept per environment, and listed in `requires.files`.

- `file.read` `{ name, encoding, offset, allowPartial }` → `{ name, contentType, size, binary,
  text | base64, … }`. Read `node['Read template'].text`. Over 5 MB set `length` or
  `allowPartial: true`.
- `file.write` `{ name, content, encoding, contentType? }`: `content` is text, so turn an object
  into text first (`{{ toJson(json) }}` or json.stringify). Writing a name again keeps the old
  versions (10).
- Binary files (PDF, images): `encoding: "base64"` on both read and write, and pass `.base64`.
- A file's fingerprint (the SHA-256 that `sha256sum` prints, as other systems store it): read with
  `encoding: "text"` and give `crypto.hash` the `.text`. `crypto.hash` hashes text as UTF-8, so the
  hash of `.base64` is a different value. A binary file has no `.text`; its `.base64` hash
  matches only hashes made the same way.
- In an expression: `{{ file("template.html").text }}`.
- `file.list` `{ prefix, … }` → `{ files: [{ name, size, … }], count }`; `file.delete` `{ name }`.

## Schemas

A schema is a named JSON document the customer creates in Flowmage (settings, mappings). Read it
with `{{ schema("Settings").apiBase }}` or `schema.get`; change it with `schema.set` /
`schema.setPath` (paths are dotted field names). List each name in `requires.schemas`.

## CSV, JSON, XML and lists

- `csv.parse` `{ text }` → `{ rows, headers, rowCount }`. With `convertTypes: false` (default)
  every cell is text: use `toNumber()` before a number column, or set `convertTypes: true`.
- `csv.build` `{ rows }` → `{ text }`; cells must be plain values.
- `json.parse` `{ text }` needs text. A webhook JSON body is already parsed: read
  `trigger.body` directly. Its output is the parsed value itself.
- `json.build` `{ fields }` builds an object; its output is that object (no wrapper).
- `xml.parse` `{ text }`: every element becomes a list, even a single one
  (`json.order[0].id[0]`); attributes start with `@_`; values stay text. `xml.build` `{ value }`
  needs an object.
- `list.filter`, `list.sort`, `list.dedupe`, `list.group`, `list.limit` all need `rows` (a list of
  objects, e.g. `{{ node['Parse CSV'].rows }}`) and give `{ rows, count }`. Their operators are
  camel case (`greaterThan`, `isNotEmpty`), unlike table filters.

<!-- source-stamp: tables=85e324cc0117 transfer=34bdb9a2d3f8 -->


---

## File: references/triggers.md

# Triggers

A trigger is not in the file and not a node. The flow starts at Start; the customer adds the
trigger after import (Triggers tab → Add trigger). Steps read the trigger's data as `trigger.*`
anywhere in the flow; the first step after Start also gets it as `json`. Kinds: manual, webhook,
schedule, workspace event, and a service trigger (Stripe, AFAS Profit), which is instant or a
watch. Field list per kind: expression-functions.md.

## Manual

`trigger` is whatever the run was started with (null by default). A test run from the editor uses
the flow's sample data. Write steps that cope with null (`trigger.body.x ?? "…"` when the flow will
also get a webhook) or that need no trigger data.

## Webhook

- Data: `trigger.body.<field>` (the parsed JSON body), `trigger.headers['x-name']` (names in lower
  case), `trigger.query.<name>`, `trigger.method`, `trigger.receivedAt`. On the first step
  `json.body.<field>` is the same; never `json.<field>`.
- A body that is not JSON arrives as `{ rawBody, contentType, size, binary }`: parse
  `trigger.body.rawBody` (csv.parse, xml.parse, json.parse). Binary bytes are not kept.
- A body over 2 MiB arrives as `trigger.file` `{ name, contentType, size, sha256 }`: read it with a
  file.read step.
- Security headers are hidden: `authorization`, `x-api-key`, `api-key`, `x-auth-token`,
  `x-access-token`, `proxy-authorization` and any name containing `signature` or `hmac` read as
  `"•••"`; `cookie` reads null; the query `key` reads `"•••"`. For a shared token, ask the caller to
  send `x-token` and check it first:

  ```json
  { "id": "check_token", "type": "core.condition", "version": 1, "label": "Token valid?",
    "config": { "expression": "{{ secret.WEBHOOK_TOKEN == trigger.headers['x-token'] }}" },
    "position": { "x": 224, "y": 0 } }
  ```

  The `false` port goes to a `core.raiseError` (or stays unwired); `WEBHOOK_TOKEN` goes in
  `requires.secrets`. The webhook can also require a key or check a signature in its own settings;
  that needs nothing in the file.

### Answering the caller

The response mode is a setting on the webhook (Async or Sync), not in the file.

- **Async** (default): the caller gets `202 { executionId }` at once.
- **Sync**: the caller waits (30 s by default, 1–120 s) and gets `200` with the run's result, `500`
  when the run fails, `504` when the wait runs out (the run carries on).
- The result is the value of a `core.return` step, else the output of the step that finished last.
  For a predictable answer, end each path in a `core.return`:

  ```json
  { "id": "reply", "type": "core.return", "version": 1, "label": "Reply",
    "config": { "value": "{{ parseJson('{\"ok\": true}') }}" }, "position": { "x": 672, "y": 0 } }
  ```

  or build the object with a json.build step first and return `{{ node['Build reply'] }}`. Tell the
  customer to set the webhook to Sync. A webhook answers one Sync flow only.
- The webhook's "Response expression" setting can shape the answer instead (no secrets in it).

## Schedule

- Set on the trigger: a cron expression (five fields, minute first: `0 7 * * 1-5` = 07:00 on
  weekdays, `*/15 * * * *` = every 15 minutes) and a time zone (default: the workspace's).
  At most once a minute.
- Data: `trigger.scheduledAt` (ISO time of the planned run), `trigger.scheduleId`. Use
  `$macro.date.iso()` for "now" and `formatDate(trigger.scheduledAt, "yyyy-MM-dd")` for a date.
- A missed run is skipped, not caught up. While a run of the flow is still going (a delay, a wait,
  an approval), the next tick is skipped.
- Write the cron and time zone into your reply; the customer types them into the trigger.

## Workspace event

Something that happens in the workspace starts the flow. Types: `run.completed`, `run.failed`,
`run.cancelled`, `alert.fired`, `alert.resolved`, `flow.put_on_prod`, `connection.broken`,
`connection.healed`, `secret.set`, `table.rows_changed`, `file.uploaded`, `file.updated`,
`member.joined`.

The customer sets it up as a sentence: "When [a run fails] in [any flow / these flows] (in test or
in prod) → start this flow". Run and flow events scope to flows, table events to tables,
connection events to connections, the rest to the whole workspace. To react to one table, tell the
customer to pick "in these tables" and that table, rather than checking `trigger.subject.id` in
the flow. Events the flow causes itself never start it; a chain of event triggers stops after
four flows. Each event trigger holds one event and where it counts: New trigger → Workspace event
(in the flow's Triggers tab or on the Triggers page) asks What happens and Where, and every flow
on that trigger starts on it. Another flow joins it through Add trigger → Existing trigger….

- Data: `trigger.type`, `trigger.occurredAt`, `trigger.subject.kind`, `trigger.subject.id`,
  `trigger.subject.name` (may be missing), `trigger.actorId` (the member who caused it, when one
  did), `trigger.event.id`.
- `table.rows_changed`: `trigger.rows` = `{ table, op, count, ids, changes }`, `op` one of
  `created`, `updated`, `deleted`, `restored`, `status`, or `mixed`; `table` is the table's id,
  `ids` the list of row ids, `changes` one `{ id, op }` per change.
  Read the rows with a table step (tables-and-files.md); a loop over `{{ trigger.rows.ids }}` gets
  one id per round.
- A run event (`run.completed`, `run.failed`, `run.cancelled`) also sets `trigger.flowId`,
  `trigger.runId` and `trigger.env`: `"test"` or `"prod"`, where that run ran (not where this
  flow runs).
- There is no form trigger and no separate "table changed" trigger: use the event.

## Service triggers

The customer picks the service and what should start the flow (New trigger → the service → the
"starts when" choices, e.g. "An invoice is paid"); they never choose how Flowmage hears of it.
The trigger says one line: "Instant" when the service tells Flowmage right away (Stripe; an AFAS
dossier update, whose link the customer pastes into AFAS), "Checked every 5 min" when Flowmage
looks (a watch). Connecting a service makes no trigger, and deleting the connection deletes the
triggers that sign in with it. Many triggers on one service share Flowmage's one registration
there; an event no trigger takes is ignored.

## Watch

A watch is the checked service trigger: it checks a list in a service and starts a run for each item added, updated or removed: the
rows of an AFAS Profit GetConnector, or the tasks of an Asana project. The customer makes it on
the Triggers page (New trigger → the service → which list, and Added, Updated and/or Removed); it
starts checking at once. Then they add it to the flow (Triggers tab → Add trigger → Existing
trigger…).

- Data: `trigger.action` (`added`, `updated` or `removed`), `trigger.key` (the value that names
  the item), `trigger.item.<field>` (the item, by the list's own field names),
  `trigger.list.service`, `trigger.list.name`, `trigger.detectedAt`, and `trigger.modifiedAt` when
  the list has a modified field.
- `updated` also sets `trigger.previous` (the item before), `trigger.changed` (the fields that
  moved) and `trigger.diff.<field>.from` / `.to`; a field too long to keep reads
  `{ "tooLong": true }` in `diff`. Otherwise `previous`, `changed` and `diff` are all null, and a
  `diff` with no moved field is null, never `{}`: test `{{ trigger.diff }}` for null before
  reading `trigger.diff.<field>`. A watch started from "Changes since a date" sends those items as
  `updated` with all three null — there is no before to compare.
- `removed` carries the item's last-seen values and `trigger.reason`: `deleted` (the service says
  it is gone) or `left` (it still exists but no longer matches the list).
- Filters belong on the watch ("Only items where", "Only these fields count"), not in the flow.
  "An invoice became Paid" is a watch filtered on Status is Paid: the invoice arrives as `added`.
- The first check only looks and starts nothing. "Include what's already there" sends the existing
  items as `added`; those, and a check that finds more than 250 items or removes over a fifth of
  them, wait for the customer's OK on the watch.

## Subflow runs

A flow called by another flow gets no trigger data; it reads `params.<name>` (subflows.md).

<!-- source-stamp: triggers=d28af2318aa9 -->


---

## File: references/troubleshooting.md

# Troubleshooting

Search this page for the code (`TABLE_CELL_INVALID`) or for words from the message. First find the stage. **Import** means the import dialog refused the file. **Publish** means the editor listed issues when you published (Send to Test or Put on Prod); they have no code, so match the sentence. **Run** means the run page shows a code, a headline and a detail.
In the messages, `<label>` stands for a node's label and `<…>` for any other value. Every fix says what to change in the `.flowmage.json` file. Make the change, then import the file again.

## Contents

- [Import refused](#import-refused)
- [Publish issues](#publish-issues): [graph shape](#graph-shape) · [ports and edges](#ports-and-edges) · [node config](#node-config) · [expressions](#expressions) · [loops, try/catch, subflows, resume URL](#loops-trycatch-subflows-resume-url)
- [Run failures](#run-failures): [control flow and limits](#control-flow-and-limits) · [HTTP and vendor nodes](#http-and-vendor-nodes) · [tables](#tables) · [files](#files) · [data nodes](#data-nodes) · [schemas](#schemas) · [subflows](#subflows) · [approvals and email](#approvals-and-email) · [connections and secrets](#connections-and-secrets) · [AI](#ai) · [SQL](#sql) · [custom nodes](#custom-nodes)
- [Not about your file](#not-about-your-file)

## Import refused

The import dialog shows the sentence with a capital letter and no code. An API answer starts with the code, for example `INVALID_IMPORT: this file …`. Import checks only the file's shape. The flow's logic is checked later, when it is published.

| Symptom | Cause | Fix in the file |
|---|---|---|
| `INVALID_IMPORT` "this export is nested too deeply to read (over 64 levels)" | A value nests more than 64 levels deep. | Flatten the deep config value. No object or array chain may go 64 levels deep. |
| `INVALID_IMPORT` "this export is damaged and cannot be read" · "That file is not valid JSON" · "That export is damaged and cannot be read" | The file is not valid JSON, `graph.nodes` is not an array, or `name` is not a string. | Write one strict JSON object: no comments, no trailing commas, double quotes. Make `graph.nodes` and `graph.edges` arrays and `name` a string. → format.md |
| `INVALID_IMPORT` "this file does not contain a flow" | The top level is an array, a string or `null`. | Make the top level a single object `{ "format": …, "formatVersion": …, … }`. |
| `INVALID_IMPORT` "this file is not a Flowmage flow export" | `format` is missing or wrong. | `"format": "flowmage.flow"`. |
| `INVALID_IMPORT` "this export was written in format version <v> and this Flowmage reads version 1" · "That export was written by a newer Flowmage" | `formatVersion` is a number above 1. | `"formatVersion": 1`. |
| `INVALID_IMPORT` "this export is damaged and cannot be read (at `formatVersion`, which must be the number 1)" · "That export is damaged and cannot be read" | `formatVersion` is missing, text such as `"1"`, or a number below 1. | `"formatVersion": 1`, as a number. |
| `INVALID_IMPORT` "this export is damaged and cannot be read (at `<path>`)" | The first shape problem is at `<path>`: a missing key, a wrong type or a value out of range. | Fix the key at `<path>`. The usual causes: `requires` lacks one of its six arrays (`connections`, `tables`, `schemas`, `files`, `secrets`, `customNodes`), so add `[]`; `exportedAt` is missing; a node lacks `id`, `type`, `version`, `label` or `config`; an edge lacks `sourcePort`; `settings.timeoutMs` is outside 1000–600000; `settings.errorHandling` is not `"fail"` or `"continue"`. → format.md |
| `INVALID_IMPORT` "this export's tags cannot be used (a tag needs a name)" · "(a tag name is at most 15 characters — '<n>' has <len>)" · "(a tag name may not contain a control character …)" | A tag is invalid. | Use at most 5 `tags`, each `{ "name": 1–15 characters on one line, "color": one of indigo, violet, fuchsia, rose, amber, emerald, teal, sky, slate }`. |
| `INVALID_IMPORT` "this export's flow settings cannot be used (description must be text)" · "(the description is <n> characters — a description is at most 500)" | `description` is invalid. | Make `description` a string of at most 500 characters, or remove it. |
| `INVALID_IMPORT` "… (subflow must be { params })" · "(subflow.enabled must be true or false)" · "(subflow.params must be a list of parameters)" · "(parameter <n> must be { name, type, default? })" | `subflow` has the wrong shape. `<n>` counts from 1. | `"subflow": { "params": [ { "name": "orderId", "type": "text" } ] }` (an old `enabled` is ignored). → subflows.md |
| `INVALID_IMPORT` "… (a subflow declares at most 25 parameters — this one has <n>)" | Too many parameters. | Keep at most 25 entries in `subflow.params`. Pass one `object` parameter instead of many loose ones. |
| `INVALID_IMPORT` "… (parameter <n>'s name '<name>' is not one {{ params.<name> }} can read …)" | The parameter name is invalid. | Use letters, digits and `_`, starting with a letter or `_`, at most 64 characters, and none of `true`, `false`, `null`, `constructor`, `__proto__`, `prototype`. Rename every `{{ params.<old> }}` too. |
| `INVALID_IMPORT` "… (two parameters are both named '<name>' …)" | A parameter name is used twice. | Give each parameter its own `name`. |
| `INVALID_IMPORT` "… (parameter '<name>' has no type this view knows …)" | `type` is unknown. | Set `type` to one of `text`, `number`, `boolean`, `object`, `list`. |
| `INVALID_IMPORT` "… (the description of parameter '<name>' must be text)" · "(… is <n> characters — a description is at most 500)" | The parameter `description` is invalid. | Make it a string of at most 500 characters. |
| `INVALID_IMPORT` "… (parameter '<name>' has a default of null …)" | `"default": null`. | Remove the `default` key. |
| `INVALID_IMPORT` "… (parameter '<name>' is a <type> parameter, and its default is not a <type>)" | The default does not match the type. | Match them, for example `"type": "number", "default": 5` or `"type": "list", "default": []`. |
| `INVALID_IMPORT` "… (the default of parameter '<name>' is <x> KiB — a default is at most 5 KiB)" | The default is too big. | Shrink the default. Keep large data in a table or file. |
| `FLOW_TOO_LARGE` "this flow is <x> MB — the limit is 1.99 MB; nothing was imported" · "“<file>” is <size> — a flow may be at most 1.99 MB." | The file is over 1,990,000 bytes. The dialog counts whitespace too. | Write compact JSON with no indentation. Move large fixed data into a table or file and read it at run time. Split the flow into subflows. → subflows.md |
| `INVALID_SETTINGS` "flow name must be 1–120 characters" · "Shorten the name to 120 characters or fewer — this one is <n>." | `name` is empty or too long. | Make `name` 1–120 characters. |
| `NAME_TAKEN` "this workspace already has flows named '<base>' up to '<base> (99)' — rename one first" | A taken name lands as `<base> (2)` … `(99)`; after that it is refused. | Give `name` a name that is not taken yet. |
| "That file could not be read — try picking it again." | The browser could not read the file. | Pick it again. Save it as UTF-8 plain text with the `.flowmage.json` extension. |

The import can land and still report what it lacks: "— N things need setup", then one line per need.

| Report line | Cause | Fix in the file |
|---|---|---|
| Connection “<name>” — add it under Connections | No connection here matches both the name and the provider exactly. The node's `config.connection` was cleared. | Make `requires.connections[]` read `{ "id": <same placeholder as config.connection>, "name": <exact name in Manage → Connections>, "provider": <exact provider key, e.g. "afas-profit", never ""> }`. Or create the connection, then pick it on the node. → connections-and-secrets.md |
| Table “<name>” — create it here and the flow will use it · Schema “<name>” — … | No table with that name (case does not matter), or no schema with it. | Create it, or correct `requires.tables[].name` or `requires.schemas[].name`. A table entry with `columns` keeps the node's column mapping. → tables-and-files.md |
| File “<name>” — add it under Storage → Files · Secret “<name>” — add it under Storage → Secrets | The file or secret is missing here. | Upload the file under Storage → Files, or add the secret under Manage → Secrets with exactly that name. Or correct the name in `requires.files` or `requires.secrets`. |
| Custom node “<name>” — install it under Custom nodes | The node type is not installed. | Install it, or remove the nodes of that type and the entry in `requires.customNodes`. |
| Flow “<name>” — import or create it first | No live flow carries exactly that name. The node's `config.flowId` was cleared. | Import the called flow first. Then import this one again, with `requires.flows[]` `{ "id": <same as config.flowId>, "name": <exact name> }`. → subflows.md |
| Tag(s) <list> is/are left off — this workspace already holds 20 tags, the most it can. | The workspace's tag limit is reached. | Reuse tag names that already exist, or drop `tags`. |
| A node's connection, table or called flow is empty after import, and the report names nothing | The id in `config` was not declared in `requires`, so the import dropped it. | Use the same placeholder id in `config.connection` and `requires.connections`, in `config.tableId` and `requires.tables`, and in `config.flowId` and `requires.flows`. Only a literal value directly under `config` is re-pointed. An expression there is not. |

## Publish issues

Publishing compiles the flow. The editor groups issues under each node's label and drops the `'<label>': ` prefix. Flow-wide issues sit under "Flow".

### Graph shape

| Symptom | Cause | Fix in the file |
|---|---|---|
| "invalid FlowGraph at '<path>': <message>" | The graph structure is wrong at `<path>`. | Fix that key of `graph`. → graph-and-wiring.md |
| "duplicate node id '<id>'" | Two nodes share an `id`. | Give every node its own `id`, and update the edges that use it. |
| "edge <source> → <target> references a node that does not exist" | An edge `source` or `target` is not a node `id`. Labels are not ids. | Point the edge at node `id`s. |
| "'<label>': unknown node '<type>@<version>' — the registry has no such version" | The `type` or `version` does not exist. | Use a `type` and `version` from the family page. → nodes/INDEX.md |
| "label '<label>' is used more than once — node refs resolve by label, so labels are unique" | Two nodes share a label. | Give each node its own `label`, and update every `node["<label>"]` that meant the renamed node. |
| "this flow has two Start nodes — …" | There are two `core.start` nodes. | Keep exactly one: `{ "id": "__start__", "type": "core.start", "version": 1, "label": "Start", "config": {} }`. |
| "'<source>' cannot connect into 'Start' — the flow begins there …" | An edge targets `__start__`. | Remove that edge. |
| "this flow has no nodes yet — add a node to the canvas first" | `graph.nodes` is empty. | Add the Start node and at least one step. |
| "every node in this flow is disabled — enable one so the flow has a first step" | Every node has `"disabled": true`. | Remove `disabled` from the steps that should run. |
| "nothing is connected to the Start node, so this flow cannot begin …" | No edge leaves Start. | Add `{ "source": "__start__", "sourcePort": "out", "target": "<first step id>" }`. |
| "every node has an incoming connection, so nothing can start …" | There is no Start node and every step has an incoming edge, or an edge leads back into the first step. | Add the Start node and an edge from it to the first step. |
| "the connections between '<a>', '<b>' run in a circle — a run would never finish …" · "'<source>' cannot connect into '<target>' — '<target>' already leads to '<source>' …" | The edges form a cycle. | Remove the edge that leads back. To repeat steps, use a `core.loop`. → loops-and-errors.md |

### Ports and edges

| Symptom | Cause | Fix in the file |
|---|---|---|
| "'<label>' has no port '<port>' — declared ports: <list>" | An edge's `sourcePort` names a port the node lacks. | Use one from the list. `core.condition` has `true` and `false`; `core.loop` has `body` and `done`; `core.tryCatch` has `try`, `success` and `catch`; `core.switch` has its case `id`s plus `else`; `core.waitFor` has `resumed` and `timeout`; `human.approve` has its option `id`s plus `timeout`; classify has its label `id`s plus `other`; most other nodes have `out`. → graph-and-wiring.md |
| "port '<port>' of '<label>' has N outgoing edges — one edge per port" | Two edges leave one port (Start's `out` too). | Keep one edge per port. Run the steps one after another instead. |
| "'<label>': two ports are both named '<port>' — give every case its own unique id" | Two switch cases, approve options or classify labels share an `id`. | Give each row its own `id`, and match the edges' `sourcePort` to it. |
| "'<label>': row N of <Field> has no id — give it one …" | A case, option or label row has no `id`. | Add `"id": "<short-name>"` and use it as that edge's `sourcePort`. |
| "'<label>': ports are not derivable — …" | The list that defines this node's ports is malformed. | Rewrite `cases`, `options` or `labels` as the node page shows. → nodes/INDEX.md |
| "'<label>' ends the run, so nothing can come after it — remove its connection to '<target>'." | An edge leaves a step that ends the run, such as `core.return`. | Remove that edge. |

### Node config

Config issues read in two ways. The API says "'<label>': required field '<path>' is missing". The editor rewords it as "<Field> is required.", "<Field> must be <text / a number / a whole number / true or false / a list / a set of name and value pairs / a date>.", "<Field> needs at least N characters.", "<Field> must be at least N / greater than N / at most N / less than N." or "<Field> must be one of: a, b."

| Symptom | Cause | Fix in the file |
|---|---|---|
| "'<label>': config must be an object" | `config` is missing or is not an object. | Set `"config": {}` at least. |
| "'<label>': required field '<path>' is missing" · "<Field> is required." | A required key is missing. | Add the key the node page marks as required. → nodes/INDEX.md |
| "'<label>': field '<path>' does not validate — <reason>" · "<Field> must be <kind>." · "<Field> must be one of: …" · "… at least N" | The value has the wrong JSON type, is out of range, or is not a listed choice. | Write the right JSON type (`5` not `"5"`, `true` not `"true"`), keep the value in range, and copy a listed choice exactly. Case matters. |
| "'<label>': field '<path>' — <path problem>" | A path field, such as a schema path, has an empty segment, uses something other than dots, or names a reserved word. | Write `a.b.c`: no leading, trailing or double dots, and no `__proto__`, `constructor` or `prototype`. |
| "'<label>': config does not validate — A delay needs a duration" | `core.delay` in `"mode": "interval"` has no `amount`. | Add `"amount": 5, "unit": "minutes"`. |
| "… A delay that waits for a moment needs that moment" | `"mode": "until"` has no `until`. | Add `"until": "2026-11-01T09:00:00Z"` or an expression that gives such a string. |
| "… A delay may not exceed 31 days" · "… A wait runs from 1 minute to 31 days" | `amount` and `unit`, or a `deadline`, are out of range. | Stay within 31 days, and a wait's `deadline` at 1 minute or more. → triggers.md |
| "… Two options cannot share an id …" · "… “timeout” is this node's built-in port — give the option another id" | `human.approve` `options` are wrong. | Give 2–5 options, each with a unique `id` that is not `timeout`. |
| "… Nobody is asked to decide — choose at least one person under Who decides, or allow an address outside the workspace" | `request.approverMembers` and `request.approverAddresses` are both empty. | Add a member or an email address. |
| "… Pick the connection this request authenticates with …" | `core.http` has `auth.kind` `"connection"` but `connection` is empty. | Set `config.connection` to a placeholder id declared in `requires.connections`. → connections-and-secrets.md |
| "… A from-address is required when you send through your own SES account." · "'<x>' is not a valid email address" | `ses.send` has a `connection` but no `from`, or holds a malformed address. | Set `from` to a verified identity, and fix the address. |
| "… Add at least one field — …" | A generate node has `"output": "fields"`, or an extract node, with an empty `fields`. | Add `fields` entries, or set generate's `"output": "text"`. |
| "… Give this at least 2 labels …" · "… Two labels cannot share an id …" · "… “other” is this node's built-in path …" | A classify node's `labels` are wrong. | Give at least 2 labels with unique `id`s, none of them `other`. |
| "'<label>': field 'code' does not validate — the JavaScript snippet does not live in config — it is the node's own `code` field" | `core.javascript` has its code under `config.code`. | Move the code to the node's top-level `"code"` and remove it from `config`. |
| "'<label>': the JavaScript node has no code — write a snippet that returns a value." | The top-level `code` is missing or empty. | Set `"code"` to a snippet that ends in `return …`. |
| "'<label>': this node ends the run on purpose, so it does not take error handling …" | `core.raiseError` carries `settings.errorHandling`. | Remove `errorHandling` from that node's `settings`. |
| "'Start' is where the flow begins — turning it off would leave nothing to run" | Start has `"disabled": true`. | Remove `disabled` from Start. |
| "'<label>' decides where the flow goes next, so there is no single path to pass through …" | A branching node has `"disabled": true`. | Delete the node and reconnect its neighbours instead of disabling it. |
| "<Field>: {{ }} is not filled in in this field — the text is sent exactly as written …" | `{{ }}` sits in a plain-text field, such as a SQL `query`. | Write `$1`, `$2` … in `query`, and put the values in order in `params`: `["{{ json.id }}"]`. |

### Expressions

API issues read "'<label>' <config path>: <message> (<code>)". → expressions.md

| Symptom | Cause | Fix in the file |
|---|---|---|
| `EXPR_SYNTAX` "Expression opened with '{{' has no closing '}}'" · "Empty expression" · "Unexpected character …" · "String literal is never closed" · "Invalid escape sequence …" · "Unexpected … after end of expression" · "Expected ':' in ternary …" · "Expected ']'" · "Expected ')'" · "Expected a property name after '.'" · "Unexpected end of expression" | The expression has a typo. | Close every `{{ }}`, quote and bracket. Inside a JSON string, write quotes as `\"` or use `'single'`. A label with spaces needs `node["My step"]`, not `node.My step`. |
| `EXPR_SYNTAX` "Only registered functions and $macro.* can be called — values are never callable" · "'<x>' is not allowed as a property name" | A value is called as a function, or the expression reads `constructor`, `__proto__` or `prototype`. | Call only listed functions, and read plain fields. → expression-functions.md |
| `EXPR_TOO_LARGE` "Field is larger than 8192 bytes (8 KB) — split it up" | One field is over 8 KB. | Shorten the field. Keep long fixed text in a file or schema and read it in an earlier step. |
| `EXPR_TOO_DEEP` "Expression nests deeper than 32 levels — flatten it" | Brackets or calls nest too deep. | Split the work across steps or flatten the expression. |
| `EXPR_UNKNOWN_NAMESPACE` "Unknown name '<n>' — expected a namespace (json, node, loop, trigger, execution, flow, workspace, secret, $macro, params) or a function name." | A bare name, such as `{{ email }}` or `{{ body.email }}`. | Start the name with a root: `json.` (this step's input), `node["<label>"].`, `trigger.body.`, `params.` or `loop.`. |
| `EXPR_UNKNOWN_FUNCTION` "'<f>' is not a registered function" · "Unknown macro '<p>' — available: $macro.random.uuid(), $macro.date.iso(), $macro.date.timestamp()" | The function or macro does not exist. | Use one from the list. → expression-functions.md |
| `EXPR_ARG_COUNT` "<f>() expects <n> argument(s), got <m>" · "<path>() takes no arguments" | The call passes the wrong number of arguments. | Pass exactly the arguments the function takes. → expression-functions.md |
| `EXPR_SECRET_MISUSE` "Secret names are always static …" · "secret.X can only be used on its own or compared with == / != …" · "Secrets cannot appear in a webhook response." | A secret is used in a computation, read with a computed key, or used in a response. | Write the secret as its own part of the text, `Bearer {{ secret.API_TOKEN }}`, not `{{ "Bearer " + secret.API_TOKEN }}`. Use `secret.NAME` or `secret["NAME"]`. → connections-and-secrets.md |
| `EXPR_UNKNOWN_SECRET` "Secret '<n>' does not exist in this workspace" | No secret with that name. | Add the secret under Manage → Secrets with that exact name and list it in `requires.secrets`, or correct the name. |
| `EXPR_UNKNOWN_NODE` "'<x>' is not an upstream node label or id" · "'<label>' code: '<x>' is not …" | The label is misspelt, or the step runs later or on another branch. | Use the exact `label` of a step that runs before this one on the same path. This applies inside JavaScript `code` too. |
| `EXPR_UNKNOWN_NODE` "'<x>' is the loop this step runs inside — …" | A step inside the loop body reads the loop's own output. | Read the current round as `loop.item`, `loop.index` or `loop.count`. |
| `EXPR_LOOP_OUTSIDE` "loop.* is only available inside a loop body" | `loop.*` is used outside a body. | Use `loop.*` only in steps reached from the loop's `body` port. After the loop, read `node["<loop label>"].collected`. → loops-and-errors.md |
| `EXPR_UNKNOWN_PARAM` "Parameter '<n>' is not declared by this flow — declared: …" · "Parameter '<n>' is not declared — this flow declares no parameters" | `params.<n>` is not in `subflow.params`. | Declare it in `subflow.params`, or correct the name. → subflows.md |
| `EXPR_UNKNOWN_TRIGGER_FIELD` "trigger.<x> is not in this flow's trigger, so every delivery would read it as null …" | The trigger carries no such field. | Webhook data is `trigger.body.<field>`, a schedule gives `trigger.scheduledAt`, a watch `trigger.item.<field>`, and the message lists what the trigger carries. → triggers.md |
| `EXPR_NOT_IN_COLLECT` "'<step>' is inside the nested loop '<inner>' — …" · "a loop's collect cannot read a secret / a file / a schema / json — …" | A loop's `collect` reads something it may not. | `collect` reads one of this round's own steps: `{{ node["<step>"].id }}`. For a nested loop, collect in the inner loop, then collect `{{ node["<inner>"].collected }}` in the outer one. → loops-and-errors.md |

### Loops, try/catch, subflows, resume URL

| Symptom | Cause | Fix in the file |
|---|---|---|
| "'<label>' has nothing in its body — …" | No edge leaves the loop's `body` port. | Add `{ "source": "<loop id>", "sourcePort": "body", "target": "<first step id>" }`. |
| "loop '<label>' is reachable from inside its own body — remove the connection that leads back to it" | A body step has an edge back to the loop. | Remove it. The last body step has no outgoing edge, and the loop repeats by itself. → loops-and-errors.md |
| "'<x>' sits inside the body of both '<A>' and '<B>' — …" | A step belongs to two loop bodies. | Keep each step in exactly one body. Duplicate it if both loops need it. |
| "'<source>' cannot connect into the body of loop '<label>' from outside …" | An edge from outside targets a body step. | Point that edge at the loop node itself. |
| "'<x>' stops the loop it is in, so nothing can come after it …" · "'<label>' stops the loop it is in, but it is not inside a loop …" | `core.break` has an outgoing edge, or sits outside a body. | Remove the edge out of the break, and put the break inside a loop body. |
| "'<label>' reads every page, so its items must be one step's list, like {{ json.records }} …" · "… and it has no single one — name the step: …" | `"allPages": true`, and `items` is not one step's list path. | Set `"items": "{{ node[\"List contacts\"].records }}"`. |
| "'<label>' cannot read every page of '<step>': that step takes no cursor or answers no next cursor …" | The step named in `items` cannot page. | Set `"allPages": false`, or loop over a list step that pages. |
| "… whose pages answer their items in <items> — loop over {{ <root>.<items> }}" | `items` points at the wrong field. | Use the path the message gives. |
| "'<label>' reads every page of '<step>' one at a time — turn off Fetch every page on '<step>'" | Both the loop and the step fetch every page. | Set the step's fetch-every-page key (such as `returnAll`) to `false`. |
| "'<label>' reads every page of a step outside its own loop scope — move that step next to the loop" | The paging step sits in another loop or try scope. | Wire the list step directly before the loop, in the same scope. |
| "'<label>' has nothing to try — …" | No edge leaves the `try` port. | Add an edge from `try` to the first guarded step, or remove the try-catch. |
| "'<x>' is in the try of '<T>' and also after it — …" | A try step also leads to a step after the try. | Remove the edge from the try steps to `<x>`. Continue from `success`. |
| "'<x>' sits inside the try of '<T>' — a try-catch cannot be nested in another try …" · "'<x>' sits inside the try of both '<A>' and '<T>' …" | A try-catch is nested in another, or a step belongs to two tries. | Move the inner try-catch after `<T>` or into its `catch`. One try per step. |
| "'<source>' cannot connect into the try of '<T>' from outside …" · "… into the catch of '<T>' …" · "'<source>' runs before '<T>' and cannot connect past it …" | An edge skips the try-catch's own entry, or a step is reached from the bodies of two tries. | Point the edge at the try-catch node itself. Only a failure leads into `catch`. → loops-and-errors.md |
| "'<label>' does not name a flow to call — …" | `core.callFlow` has an expression in its `flowId`. | Set `flowId` to a literal placeholder id, declared in `requires.flows` with the called flow's exact name. → subflows.md |
| "'<label>' calls this flow itself — …" · "'<label>' calls '<name>', which already leads back to this flow …" | The calls form a circle. | Call a different flow. |
| "'<label>' calls a flow that no longer exists — pick another flow." | The flow behind `flowId` was deleted. | Import or create the flow, then fix `requires.flows`. |
| "'<label>' calls '<name>', which makes a chain of N calls — at most 3 are allowed …" | The subflows nest too deep. | Call a flow that calls fewer, or flatten one level. |
| "'<label>': a wait inside a loop cannot be resumed by URL …" | `core.waitFor` with `"resumeUrlEnabled": true` sits in a loop body. | Set `resumeUrlEnabled` to `false`, or move the wait outside the loop. |
| "'<label>': a flow has one resume URL, and '<first>' already carries it …" | Two waits have `resumeUrlEnabled` set to `true`. | Keep it `true` on one wait only. |

## Run failures

The run page shows the code, a headline (quoted below) and a detail. A step with `"settings": { "errorHandling": "continue" }` hands its error on instead of failing the run. A `core.tryCatch` sends a failure to `catch`. → loops-and-errors.md

A node code that is not on this page is in nodes/ERRORS.md, under the nodes that raise it, with its sentence.

### Control flow and limits

| Symptom | Cause | Fix in the file |
|---|---|---|
| `DRAFT_INVALID` "This flow can't run yet — open the issue list in the status bar, or add your first node to an empty canvas." | A manual run of the draft found publish issues. | Fix each issue ([Publish issues](#publish-issues)). |
| `FLOW_ERROR` "This flow raised an error on purpose — the node that raised it says why." | A `core.raiseError` step was reached. | If that is wrong, fix the condition or switch that leads to it. Its `message` and `code` set what the run shows. |
| `EXPRESSION_ERROR` "An expression in a node's settings could not be evaluated …" with "Field '<f>' expects a <type>, but <expr> evaluated to null …" or "<fn>() got null for '<param>' …" | A value was missing at run time. | Give a fallback: `{{ json.email ?? "" }}` or `default()`, or route on `{{ json.email != null }}` first. |
| `EXPRESSION_ERROR` "'+' needs two numbers or two strings …" · "'!' needs a boolean …" · "a condition must evaluate to a real boolean, got …" | The types do not match. | Convert the value first, and test that a field exists with `json.x != null`. → expression-functions.md |
| `EXPRESSION_ERROR` "Division by zero …" · "Array index must be a whole number …" · "Evaluation exceeded 1000 steps — simplify the field" | Bad arithmetic, a bad index, or an expression too heavy to evaluate. | Guard the divisor, round the index, or move heavy work into a `core.javascript` step. |
| `EXPRESSION_ERROR` "config validation failed: …" · "'<value>' is not a date — use an ISO 8601 instant" | The evaluated value breaks the field's rules. | Make the expression give the right shape, for example a delay `until` of `"2026-11-01T09:00:00Z"`. |
| `INVALID_LOOP_ITEMS` "A loop did not receive a list of items — check the loop node's items field." | `items` did not give an array. | Point `items` at a list: `{{ node["List orders"].records }}`. |
| `ITERATIONS_EXCEEDED` "A loop had more items than a run allows — narrow the list before the loop." | The loop has too many items. | Filter or limit before the loop (`list.filter`, `list.limit`), or page with `"allPages": true`. |
| `LOOP_ITEMS_TOO_LARGE` "… pass less data per item." | The items list is too large in bytes. | Loop over ids only, and fetch each record inside the body. |
| `LOOP_COLLECT_TOO_LARGE` "One round of a loop collected more than a round may hold — collect one field, not the whole step." | `collect` takes a whole step. | Collect one field: `{{ node["<step>"].id }}`. |
| `COLLECTED_TOO_LARGE_FOR_NODE` "… loop over that list instead." | A later step reads the whole `collected` list at once. | Loop over `node["<loop>"].collected`, or collect less per round. |
| `WAIT_TIMED_OUT` "Nobody answered this step before its deadline, and it is set to fail when that happens." | `core.waitFor` or `human.approve` has `"onTimeout": "fail"`. | Set `"onTimeout": "continue"` and wire the `timeout` port, or lengthen `deadline`. |
| `NODE_TIMEOUT` "A node ran past its time limit and was stopped — the system it calls may be slow." | The step ran past `settings.timeoutMs`. | Raise `settings.timeoutMs` (at most 600000), or ask the system for less. |
| `OUTPUT_TOO_LARGE` "A node returned more data than a run can carry — return less, or write a file." | One step's output is too big. | Ask for fewer fields or pages, or on `core.http` set `"responseFormat": "file"`. |
| `OUTPUT_TOO_LARGE_TO_REFERENCE` "An expression here reads an earlier output too large to load …" | An expression reads a huge earlier output. | Read one small field, or have the earlier step write a file. |
| `OUTPUT_NOT_SERIALIZABLE` "This node returned a value that cannot be stored as JSON — return plain data instead." | Often JavaScript code returns a `Date`, `Map`, function or `undefined`. | Return plain objects, arrays, strings and numbers, for example `date.toISOString()`. |
| `RESPONSE_TOO_LARGE` "… ask it for less, page by page." | The other system's answer was too large. | Use a smaller page size or fewer fields, and page with a loop's `allPages`. |
| `RUN_LIFETIME_EXCEEDED` "… not finished 100 days after it started — shorten its waits and delays." | The waits add up to more than 100 days. | Shorten the `deadline` and `amount` values. |
| `NOT_ENGINE_EXECUTED` "This node cannot run on its own — run the flow instead." | A step that only works inside a run was run alone. | Nothing to change in the file. Run the flow. |

### HTTP and vendor nodes

| Symptom | Cause | Fix in the file |
|---|---|---|
| `HTTP_<status>` "The system this flow called answered HTTP <status>." | The other system refused the request. | Check `url`, `method`, `headers`, `query`, the body and `auth`; a 401 or 403 means credentials. To carry on anyway, set `settings.errorHandling` to `"continue"` on that step, or wrap it in a try-catch. |
| `HTTP_INVALID_BODY` "The request body could not be built — check the node's body field." | The body does not fit `bodyEncoding`. | Match them: a JSON value for JSON, name and value pairs for a form. |
| `HTTP_RESPONSE_TOO_LARGE` "… read it in pages, or save it as a file." | The answer is too large. | Set up `pagination`, or `"responseFormat": "file"` with a `fileName`. |
| `HTTP_PAGINATION_STOPPED` "The API had more pages than “Stop after” allows — raise it, or narrow the request." | The request hit `pagination.maxPages`. | Raise `pagination.maxPages` (at most 100), or narrow `query`. |
| `HTTP_PAGINATION_TOO_LARGE` "All the pages together are too large to keep …" | All pages together are too large. | Narrow the request, or lower `pagination.maxPages` and loop. |
| `HTTP_PAGINATION_UNUSABLE` "… check “Items are in” and the paging fields." | The paging settings do not match the answer. | Fix `pagination.itemsPath` and the paging keys. |
| `HTTP_PAGINATION_CROSS_ORIGIN` "The next-page link points at a different site, so it was not followed …" | The next-page link points to another host. | Use page, offset or cursor paging on the same host. |
| `PATH_SEGMENT_INVALID` "An id on this node is empty or is not a single id — Flowmage refused it before sending anything." | An id field is empty, or holds `/` or a list. | Point it at the right field, and route empty ids away with a `core.condition`. |
| `MULTIPART_INVALID_CONTENT_TYPE` "The file's content type is not a media type …" | The upload's content type is wrong. | Set `contentType` to a media type such as `application/pdf`. |
| `PAGE_CURSOR_REPEATED` "The system pointed back at a page this read had already followed …" | The other system sent the same cursor twice. | Run it again, or page with a loop's `allPages`. |
| `PAGE_RATE_WINDOW_FULL` "The system's rate window was full — the read waits for room and resumes …" | A rate limit; the read resumes by itself. | Nothing to change. Read fewer pages if it keeps happening. |
| `EGRESS_BLOCKED` "The target system blocked the request from our network — try the node's fixed IP address option." | The target blocks unknown addresses. | Set `"settings": { "useFixedIp": true }` and let both fixed IP addresses in at the target. |
| `EGRESS_REFUSED` "This request was not allowed to leave Flowmage — check the node's address and network settings." | The address is not allowed, or fixed IP is on where none is set up. | Use a public address, not a private or local one. If the detail names "Use fixed IP address", set `useFixedIp` to `false`. |
| `EGRESS_TRANSPORT` "The target could not be reached — check that the address is right and the system is up." | The host is unreachable. | Fix `url` or the connection's address. |
| `<VENDOR>_AUTH` · `<VENDOR>_MISSING_SCOPE` "<Vendor> rejected the connection's token / credentials …" · "<Vendor> refused the session for …" | The connection is not allowed in. | Nothing in the file. Test the connection under Manage → Connections. |
| `<VENDOR>_NOT_FOUND` "<Vendor> knows no record with that id." · "<Vendor> refused the request (HTTP n) — check the node's settings." | The id or settings are wrong. | Fix the id expression and config keys on that node. → nodes/INDEX.md |
| `<VENDOR>_THROTTLED` · `<VENDOR>_UNAVAILABLE` "<Vendor> answered HTTP 5xx …" · `<VENDOR>_INVALID_RESPONSE` · `<VENDOR>_RESPONSE_TOO_LARGE` | The system was rate-limited (retried by itself), down, or answered strangely or too much. | Wait and run again; for too large, ask for less per page. |

### Tables

| Symptom | Cause | Fix in the file |
|---|---|---|
| `TABLE_NOT_FOUND` "no table '<id>' in this workspace" | `config.tableId` names no table here: it was cleared at import, never declared, or misspelt. | Declare `requires.tables[]` `{ "id": <same placeholder as config.tableId>, "name": <exact table name> }`, then create that table. → tables-and-files.md |
| "the table '<name>' is not on Prod yet — put it on Prod from Test first" | The table exists only in Test. | Nothing in the file. Put the table on Prod under Storage → Tables. |
| `TABLE_TOO_MANY_ROWS` "This select matched more rows than one step returns …" | Too many rows matched. | Narrow `filters`, set `maxRows`, or use `"ifMoreRows": "first"` and continue with `cursor`. |
| `TABLE_ROW_NOT_FOUND` "A row this flow updates no longer exists …" | `rowId` points at a deleted row. | Select the row first, or set `settings.errorHandling` to `"continue"`. |
| `TABLE_UNKNOWN_COLUMN` "A column this flow writes does not exist …" | A `values` key is not a column key. | Use the column keys, not the display names, as `requires.tables[].columns` lists them. |
| `TABLE_REQUIRED_MISSING` "A required column got no value …" | A required column has no value. | Add it to `values`, with a fallback such as `{{ json.name ?? "" }}`. |
| `TABLE_CELL_INVALID` "A value does not fit its column's type …" | A value has the wrong type for its column, or a text is over 64 KB. | Convert it: a number for a number column, an ISO date for a date column. |
| `TABLE_UNIQUE_VIOLATION` "Another row already holds this value in a column that takes unique values …" | The value is a duplicate. | Select first, and update when the row exists. |
| `TABLE_ROW_CONFLICT` "This row was changed by something else mid-run …" | `expectedVersion` no longer matches. | Re-read the row just before the update and pass its version, or drop `expectedVersion`. |
| `TABLE_ROW_TOO_LARGE` "… store the bulk as a file." | The row is too large. | Write the large part with `file.write`, and keep its name in the row. |
| `TABLE_BATCH_TOO_LARGE` "This write touches more rows than one call allows …" | The write covers too many rows. | Write in smaller groups, one group per loop round. |
| `TABLE_IDEMPOTENCY_CONFLICT` "The same write was replayed with different values — the first one stands." | A retried write produced other values. | Keep the values of a write the same on every retry: no random values or current times in `values`. |
| `TABLE_FILTER_INVALID` "A filter on this read is not valid …" | A filter is malformed. | Give each filter `{ "column", "operator", "value" }` with a snake_case operator. `is_empty` and `is_not_empty` take no value. |
| `TABLE_FILTER_INCOMPATIBLE` "A filter does not fit its column's type …" | The operator does not suit the column type. | Use an operator that fits, and a value of the column's type. |

### Files

| Symptom | Cause | Fix in the file |
|---|---|---|
| `FILE_NOT_FOUND` "… check the name under Storage → Files." | `name` matches no file. | Use the exact `name`, including its folder prefix, and list it in `requires.files`. → tables-and-files.md |
| `FILE_ARCHIVED` "A file this flow reads is archived — restore it under Storage → Files." | The file is archived. | Nothing in the file. Restore the file. |
| `FILE_TOO_LARGE` "… work with it in parts." | The file is too large for this step. | Read it in parts with `offset` and `length`. |
| `FILE_TEXT_TOO_LARGE` "… read it in parts with Offset and Length, or allow a partial read." | The text is too large to read at once. | Set `offset` and `length`, or `"allowPartial": true`. |
| `FILE_CONTENT_INVALID` "… check its format." | The content does not match `encoding`. | Use `"encoding": "base64"` for PDFs and images, on both `file.write` and `file.read`. |
| `FILE_LIST_TOO_LARGE` "… narrow the prefix, or list them page by page." | Too many files match. | Narrow `prefix`, or set `ifMoreFiles` and page. |

### Data nodes

| Symptom | Cause | Fix in the file |
|---|---|---|
| `JSON_INVALID` "A document this flow read is not valid JSON — check what produced it." | `json.parse` got broken text, or a value that is already an object. | If the input is already an object, drop the parse step and read its fields directly. Otherwise point `text` at the raw JSON string. |
| `JSON_TOO_LARGE` | The JSON document is too large. | Ask the source for less, or work from a file. |
| `JSON_TOO_DEEP` "A value nests too deeply to write as JSON text — flatten it …" | `json.stringify` got a value that nests too deep. | Stringify a flatter `value`, built with `json.build`. |
| `CSV_INVALID` "… the detail names the line." | The CSV is malformed on that line. | Set `delimiter` and `headers` to match the source, and fix unclosed quotes at the source. |
| `CSV_TOO_LARGE` "… split the export, or loop over its files." | The CSV is too large. | Split it into files and loop over them. |
| `XML_INVALID` "An XML document this flow read is not valid — the detail names the element." | The XML is malformed, or `xml.parse` got an already-parsed value. | Point `text` at the raw XML string. If the input is already an object, drop the parse step. |
| `XML_TOO_LARGE` "An XML document was too large to parse — ask the source for fewer records." | The XML is too large. | Ask the source for fewer records per call. |
| `LIST_INVALID` "A list node got something that is not a list of rows — check what feeds it." | `rows` is not an array of objects. | Point `rows` at the list itself: `{{ node["List orders"].records }}`. |
| `CRYPTO_INVALID` "… check the text, the token or the secret." | `text`, `jwt` or `auth.secret` is empty or malformed. | Point them at real values, with the key as `{{ secret.NAME }}`. |
| `JWT_INVALID` "A token did not pass its signature or expiry check — the detail says which." | The signature or the algorithm does not match. | Use the issuer's `algorithm` and its secret. |
| `JWT_EXPIRED` "A token has expired — ask the issuer for a fresh one." | The token is past its expiry. | Get a fresh token. When you sign one yourself, set a longer `expiresIn`, such as `"1h"`. |
| `JS_ERROR` "A JavaScript node threw — its own message says what went wrong." | The code threw an error. `node["X"]` for a name that is no step is `undefined`, so reading a field of it throws. | Fix `code`, and guard fields that may be missing before you use them. |
| `JS_INPUT_TOO_LARGE` | The step was handed too much. | Pass only the fields the code needs, or pass a file name. |
| `JS_OUTPUT_TOO_LARGE` | The code returned too much. | Return less, or write the bulk with `file.write`. |

### Schemas

| Symptom | Cause | Fix in the file |
|---|---|---|
| `SCHEMA_NOT_FOUND` "… create it under Storage → Schemas." | `name` matches no schema. | Use the exact `name`, list it in `requires.schemas`, and create the schema. → tables-and-files.md |
| `SCHEMA_PATH_INVALID` "A path into a schema does not exist — check the field that names it." | `path` is not in the document. | Correct `path`, or on `schema.setPath` set `"createMissing": true`. |
| `SCHEMA_TOO_LARGE` "… use a table or a file." | The document would grow too large. | Store lists in a table, or bulk data in a file. |
| `SCHEMA_READ_ONLY` "Replaying a node's configuration may not write a schema …" | A replay of a single step tried to write. | Nothing in the file. Run the whole flow. |

### Subflows

| Symptom | Cause | Fix in the file |
|---|---|---|
| `SUBFLOW_NOT_CALLABLE` "The flow this node calls is gone …" | The called flow was deleted. | Import it again, or point `flowId` and `requires.flows` at another flow. → subflows.md |
| `SUBFLOW_CYCLE` "… loop back to a flow already in the chain …" · `SUBFLOW_DEPTH` "… nest subflows deeper than a run allows …" | The calls form a circle, or nest too deep. | Call a flow that does not lead back here and calls fewer flows. |
| `SUBFLOW_PARAMS_INVALID` "A value passed to the subflow does not fit its parameter …" | A `params` value has the wrong type. | Make each `params.<name>` match the called flow's parameter `type`. |
| `SUBFLOW_DRAFT_INVALID` "The draft of the flow this node calls cannot run — fix it there, or call its test version." | `"editorRuns": "draft"`, and the called draft is broken. | Fix the called flow, or set `"editorRuns": "version"`. |
| `SUBFLOW_FAILED` "… open its run to see why." | The called flow failed. | Fix the called flow, following its own run. |
| `SUBFLOW_CANCELLED` "The subflow this node called was cancelled before it finished." · `SUBFLOW_LOST` "… re-run to call it again." | The called run was stopped or is gone. | Nothing in the file. Run again. |
| `SUBFLOW_RESUME_URLS_EXCEEDED` "… return the decision, not the link." | The subflow hands back resume links. | Have the subflow wait itself and `core.return` the decision. |
| `SUBFLOW_BUSY` "The flow this node calls skips a run while it is busy …" | The called flow's Flow settings skip runs while it is busy. | Change that flow's run limit in its Flow settings, or call it with `"mode": "detach"`. |

### Approvals and email

| Symptom | Cause | Fix in the file |
|---|---|---|
| `APPROVAL_NO_RECIPIENT` "Nobody can answer this request — every person it names has left the workspace, or none was named." | Nobody can be asked. | Fill `request.approverMembers` with current members, or `request.approverAddresses`. |
| `APPROVAL_LIMIT` "… decide or cancel some before asking for more." | Too many requests are still open. | Nothing in the file. Decide or cancel open requests. |
| `SES_INVALID_ADDRESS` "An e-mail address on this message is not valid — check To, CC and BCC." | An address is malformed. | Fix `to`, `cc` and `bcc`, and leave out empty values. |
| `SES_TOO_MANY_RECIPIENTS` "… more recipients than one send allows — split it." | Over 25 addresses across To, Cc and Bcc on `ses.send@2` (5 on `@1`). | Send per recipient inside a loop. |
| `SES_INVALID_ATTACHMENT` "An attachment could not be read …" | An attachment has a blank name, or content that is not base64. | Give every attachment a name and base64 content, for example from `"encoding": "base64"` on `file.read`. |
| `SES_ATTACHMENTS_TOO_LARGE` | The attachments are over 7 MB in total. | Send a link to a file instead of the file. |
| `SES_FROM_REQUIRED` "Sending through your own SES account needs a From address — fill it in on the node." | `connection` is set and `from` is empty. | Set `from` to a verified identity. |
| `SES_SEND_FAILED` "The mail service refused this message — its own reason is in the detail." | The mail service refused the message. | Act on the reason in the detail, usually the `from` identity or an address. |
| `MAIL_DAILY_LIMIT` "Today's flow emails through the platform sender are used up …" | The platform sender's daily quota is used up. | Wait for 00:00 UTC, or set `connection` to your own AWS SES connection, together with `from`. |

### Connections and secrets

| Symptom | Cause | Fix in the file |
|---|---|---|
| `SECRET_MISSING` "A secret this flow needs is not set — add it under Manage → Secrets." | The secret is not set in this environment. | Add it under Manage → Secrets. Keep the exact name in `requires.secrets` and in `{{ secret.NAME }}`. → connections-and-secrets.md |
| `CONNECTION_NOT_SELECTED` "No connection is selected — pick one in the node's settings." | `config.connection` is empty, usually cleared at import. | Use the same placeholder id in `config.connection` and `requires.connections`, with the exact name and provider. |
| `CONNECTION_NOT_FOUND` "This connection no longer exists — pick another in the node's settings." | The connection was deleted. | Fix the `requires.connections` entry, or pick another connection on the node. |
| `CONNECTION_MISSING` "… has no credentials for this environment — add them under Manage → Connections." | The connection has no credentials for Test or Prod. | Nothing in the file. Add the credentials for that environment. |
| `CONNECTION_BROKEN` "The provider rejected the stored credentials …" · `CONNECTION_DECRYPT_FAILED` "… stored credentials could not be read …" | The stored credentials are wrong or unreadable. | Nothing in the file. Re-enter them under Manage → Connections. |
| `CONNECTION_RECONNECT_REQUIRED` "This connection's sign-in is no longer valid …" · `CONNECTION_CONSENT_REQUIRED` "This provider needs a one-time consent …" | The sign-in expired, or consent is missing. | Nothing in the file. Reconnect, or grant consent on the connection. |
| `CONNECTION_TENANT_NOT_FOUND` "The address answered a web page instead of data …" | The connection's address is wrong. | Nothing in the file. Fix the address under Manage → Connections. |
| `CONNECTION_UNREACHABLE` "The provider could not be reached — try again in a moment." | The provider is unreachable for now. | Nothing in the file. Run again later. |

### AI

| Symptom | Cause | Fix in the file |
|---|---|---|
| `AI_DISABLED` "AI steps are turned off for this organization — an owner can enable them in Settings." | AI is not switched on for the workspace. | Nothing in the file. An owner switches AI on in Settings. |
| `AI_PROMPT_CONTAINS_SECRET` "A secret would have gone to the model, so nothing was sent — the detail names the field." | A secret reaches `prompt`, `instructions` or `text`. | Remove `{{ secret.… }}` from every field that is sent to the model. |
| `AI_MODEL_NOT_ALLOWED` "… pick another in the node's Model field." | The `model` is not allowed here. | Set `model` to an allowed model id of that provider. |
| `AI_KEY_REJECTED` "The provider rejected your key …" | The connection's key is wrong. | Nothing in the file. Re-enter the key under Manage → Connections. |
| `AI_QUOTA_EXHAUSTED` "Your AI provider account is out of credit or quota — add credit, or wait for the quota to reset." | The provider account behind the key is out of credit or past its quota. | Nothing in the file. Add credit at the provider, or wait for the quota to reset. |
| `AI_INPUT_TOO_LARGE` "The text is longer than the model can read …" | The input text is too long. | Point `text` at the part that holds the values, split it in a loop, or pick a larger `model`. |
| `AI_FILE_UNSUPPORTED` "This vendor cannot take this file — send PDF, JPEG/PNG/GIF/WebP, Office/OpenDocument/HTML/XML/CSV or text." | The `file` type is not supported. | Convert the file to a listed type. |
| `AI_OUTPUT_INVALID` "The model's answer did not fit the fields you asked for, even after one repair." | The answer did not fit the `fields`. | Simplify `fields`, give each one a `description`, and mark fewer as `required`. |
| `AI_NO_MATCH` "Nothing matched any label, and this node is set to fail when nothing fits." | `"whenNoMatch": "fail"`. | Set `"whenNoMatch": "other"` and wire the `other` port, or sharpen the labels' descriptions. |
| `AI_RUN_LIMIT` "This run reached its AI limit …" | The run used too many AI steps. | Use fewer AI steps per run. Split the work into subflows. |
| `AI_RATE_LIMITED` "Too many AI steps at once — this one never left, and it is retried on its own." | Too many AI steps ran at once. | Nothing to change. |
| `AI_PROVIDER_UNAVAILABLE` "The AI service was busy or down and answered nothing — this step never left, and it is retried on its own." | The provider was overloaded or failing and answered nothing. | Nothing to change. |
| `AI_PROVIDER_ERROR` "The model provider refused this request — the detail carries its own reason." | The provider refused the request for a reason a wait does not fix. | Read the provider's reason in the detail. Running again unchanged does not help. |

### SQL

| Symptom | Cause | Fix in the file |
|---|---|---|
| "'<name>' is not a SQL database connection — pick a <engine> connection." | The connection is not a database of this engine. | Use a `requires.connections` entry with provider `postgres`, `mysql` or `mssql`, matching the node family. |
| `SQL_READ_ONLY_VIOLATION` "This query tried to change data in Select rows — choose Execute statement …" | The query changes data with `"operation": "select"`. | Set `"operation": "execute"`. |
| `SQL_TOO_MANY_ROWS` "… add a LIMIT or WHERE, raise Max rows, or return the first rows." | The query returned too many rows. | Add `LIMIT` or `WHERE` to `query`, raise `maxRows`, or set `"ifMoreRows": "first"`. |
| `SQL_RESULT_TOO_LARGE` "… select fewer columns or rows." | The result is too large. | Select only the columns you need, in pages. |
| `SQL_PARAMS_INVALID` "… Parameters must be a list of at most 1,000 plain values." | `params` is not a plain list. | Make `params` an array of strings, numbers, booleans or nulls, one per `$n`. Pass JSON as a string with `$1::jsonb`. |
| `SQL_QUERY_FAILED` "The database refused the query — its own message is in the detail." | The database refused the SQL. | Fix `query`, its `$n` placeholders and its table or column names. |
| `SQL_TIMEOUT` · `SQL_CONNECTION_LOST` "… a statement that changes data may still have run, so check first." | The query was slow or cut off. | Check the database first. Then narrow the query or raise `settings.timeoutMs`. |
| `SQL_CONNECT_FAILED` "… check the host, port and that it lets in both fixed IP addresses." · `SQL_AUTH_FAILED` · `SQL_TLS_FAILED` | The host, port, user, password or encryption setting is wrong. | Nothing in the file. Fix the connection under Manage → Connections, and let both fixed IP addresses in. |

### Custom nodes

| Symptom | Cause | Fix in the file |
|---|---|---|
| `CUSTOM_NODE_NOT_FOUND` "… not installed here — install it, then activate the flow again." | The node type is not installed. | Install it, and keep its type in `requires.customNodes`. |
| `CUSTOM_NODE_PIN_MISSING` "… activate it again." · `CUSTOM_NODE_NOT_RUNNABLE` "… reinstall it, or activate a version that has a bundle." | The active version has no usable build of the node. | Nothing in the file. Reinstall the node and activate again. |
| `CUSTOM_NODE_CONFIG_FIELD_RESERVED` "… names a config field after a reserved JavaScript name — rename that field." | The node definition uses a reserved name. | The node's author renames the field. Then rename the same key in this node's `config`. |
| `CUSTOM_NODE_BUNDLE_MISSING` · `CUSTOM_NODE_BUNDLE_TAMPERED` · `CUSTOM_NODE_BUNDLE_TOO_LARGE` · `CUSTOM_NODE_BUNDLE_FORBIDDEN_IMPORT` | The custom node's build is missing, altered, too large, or imports something. | Nothing in the flow file. The node's author rebuilds it with no imports and within the size limit, then you reinstall it. |

## Not about your file

Nothing in the `.flowmage.json` changes these. Try again; if a problem stays, contact support with the run's code.

| Symptom | Cause | Fix |
|---|---|---|
| `ENGINE_INTERNAL` · `NODE_UNEXPECTED` "… re-run to see if it repeats." | Flowmage hit an unexpected fault. | Run again. If it repeats, contact support. |
| `OUTPUT_STORE_UNAVAILABLE` · `FILE_STORE_MISCONFIGURED` · `AI_NOT_CONFIGURED` | A Flowmage service was unavailable or not set up. | Run again later. If it stays, contact support. |
| `AI_REDACTION_UNAVAILABLE` · `APPROVAL_REDACTION_UNAVAILABLE` | The secret check could not run, so nothing was sent. | Nothing to do. The step retries by itself. |
| `PLAN_NEWER_THAN_BUILD` · `PLAN_NOT_RUNNABLE` "… deploy it again." | The deployed version does not fit the running build. | Wait, or publish the flow again. |
| `UNKNOWN_SIDE_EFFECT` "A platform update interrupted a node mid-call — the other system may have received it, so check there first." | An update interrupted a call partway. | Check the other system before you run again. |
| `NODE_RESTARTS_EXHAUSTED` "… it needs to be fixed, not re-run." | A step crashed its run environment every time it was tried. | Give a JavaScript or custom step less input. If it persists, contact support. |
| `CANCELLED` "This run was cancelled." | Someone cancelled the run. | Run again if that was not intended. |
| `CONCURRENCY_LIMIT` · `FLOW_BUSY` · `FLOW_QUEUE_FULL` | Too many runs at once, for the workspace or for this flow. | Wait, raise the flow's limit in its Flow settings, or upgrade the plan. |
| `DRAFT_STALE` "This flow was changed somewhere else, so nothing ran — reload it to run the latest version." | The flow changed in another tab or by another member. | Reload the flow and run again. |
| `CONCEPT_ONLY_IN_TEST` · `NO_ACTIVE_VERSION` | A draft runs only in Test, or nothing is active in this environment. | Publish the flow to that environment (Send to Test, or Put on Prod). |
| `PLAN_LIMIT_REACHED` "Your plan's limit is reached …" · "the <plan> plan allows up to <n> active flows per workspace …" | The plan's limit is reached. | Take another flow off, or upgrade the plan. |
| `INVALID_SETTINGS` "this version answers the sender with its result at a webhook where another flow now listens …" | Two flows share a webhook that answers the sender. | In the Triggers tab, switch that response to async, or give this flow its own webhook. |
| A table definition is out of date ("… refresh the definition"), or the workspace holds as many tables as it may | The table changed during the run, or the table limit is reached. | Run again, or remove tables you no longer use. |

<!-- source-stamp: compile=0bb4f9e964c5 expressions=c785b5463c97 transfer=34bdb9a2d3f8 runtime=ebb2f787556b tables=85e324cc0117 -->

