04. Integration Catalog¶
The complete list of things a CALL node can do.
Put one of the ids below in integration:, then fill input: with the values that integration asks for.
- id: node-id
name: Node name
type: CALL
integration: llm_chat # ← here
input: # ← every integration asks for different fields
...
The full list¶
No connection needed¶
| id | Name | Category | What it does |
|---|---|---|---|
http_request |
HTTP request | HTTP | Call any HTTP API directly |
llm_chat |
LLM Chat | AI | Have AI summarize, classify or write (the API key goes in a secret) |
transform_jmespath |
Transform (JMESPath) | DATA | Shape and filter JSON data |
dataset |
Dataset | DATA | Read and write workspace datasets |
Requires a connection¶
| id | Name | Category | Connection | Sends |
|---|---|---|---|---|
slack_post_message |
Send Slack message | MESSAGING | Slack | ⚠️ |
kakao_send_memo |
Send KakaoTalk memo to self | MESSAGING | KakaoTalk | ⚠️ |
github_list_workflow_runs |
List GitHub Actions runs | HTTP | GitHub | |
github_get_workflow_run |
Get a GitHub Actions run | HTTP | GitHub | |
github_list_run_jobs |
List jobs in a run | HTTP | GitHub | |
github_rerun_failed_jobs |
Re-run failed jobs | HTTP | GitHub | ⚠️ |
github_dispatch_workflow |
Dispatch a workflow | HTTP | GitHub | ⚠️ |
github_list_pull_requests |
List pull requests | HTTP | GitHub | |
github_create_issue_comment |
Comment on an issue or PR | HTTP | GitHub | ⚠️ |
A ⚠️ under "Sends" marks a side effect — a message actually goes out, something is written where others can see it, or CI runs. When you run a workflow through an AI assistant (MCP), these do not run straight away: you are asked to approve once.
Start with the ones that need no connection. With just
http_request·llm_chat·transform_jmespathyou can build most automations end to end. Set up OAuth connections only when you really need that service.Checking the current list: this catalog grows. To see what is available in your workspace right now, look at the node palette in the visual editor, or — if you have connected an AI assistant — just ask it "what integrations can I use?".
http_request¶
Calls any HTTP API directly. The most flexible integration, and the one you will use most.
Input¶
| Field | Required | Description |
|---|---|---|
uri |
✅ | The address to call. Expressions allowed |
method |
✅ | GET · POST · PUT · DELETE · PATCH |
mediaType |
Content-Type. Defaults to application/json |
|
queryParams |
Query string (?a=1&b=2) |
|
body |
Request body | |
authenticate |
A bundle of auth headers (see below) |
Example — reading¶
- id: fetch-orders
name: Fetch orders
type: CALL
integration: http_request
timeout: 10s
input:
uri: "https://api.example.com/v1/orders"
method: GET
queryParams:
status: "PAID"
from: "${nodes.trigger.response.body.windowStart}"
authenticate:
authMethod: HEADERS
data:
Authorization: "Bearer ${secrets.EXAMPLE_API_TOKEN}"
Example — sending¶
- id: post-hook
name: Notify an external system
type: CALL
integration: http_request
input:
uri: "https://hooks.example.com/notify"
method: POST
body:
orderId: "${nodes.trigger.response.body.orderId}"
status: "confirmed"
Output¶
The response body is the output, as-is.
${nodes.fetch-orders.response.body.data} # the data field of the response body
${nodes.fetch-orders.response.body.items.0.name} # the name of the first array element
Redirects (3xx)¶
301, 302, 303, 307 and 308 are followed for you (up to 5 hops).
A 3xx that cannot be followed — no Location header, or 300 / 304 — fails the node.
Letting it through as a success would leave an empty response body, so the real cause would surface
as an expression error one node later. If you hit this, put the final URL in uri directly.
API keys always go through
${secrets.*}. If you write a token straight into YAML, it leaks along with the workflow whenever you copy or share it. → 08. Variables and Secrets
llm_chat¶
Calls AI. Use it for summarizing, classifying, changing tone, drafting and so on.
It is not tied to a specific vendor — you pick an API shape (contract) instead. Any provider that matches the shape works.
Input¶
| Field | Required | Description |
|---|---|---|
apiContract |
✅ | OPENAI_CHAT or ANTHROPIC_MESSAGES |
model |
✅ | The model id. For example: gpt-4o-mini, claude-sonnet-4-6 |
apiKey |
✅ | The API key. Pass it via ${secrets.*} |
userPrompt |
✅ | What you are asking the AI to do |
systemPrompt |
Sets the role and the rules | |
endpoint |
If omitted, the default address for the contract | |
maxTokens |
Maximum response length. ANTHROPIC_MESSAGES sends 4096 when omitted |
|
temperature |
0–2. Only sent when you set it |
If you omit endpoint, this is where the call goes.
apiContract |
Default address |
|---|---|
OPENAI_CHAT |
https://api.openai.com/v1/chat/completions |
ANTHROPIC_MESSAGES |
https://api.anthropic.com/v1/messages |
Anywhere else works too as long as the contract is compatible (Fireworks, Together, your own vLLM instance, and so on). Just put the address in endpoint.
Example¶
- id: summarize
name: Sales summary
type: CALL
integration: llm_chat
timeout: 30s
input:
apiContract: OPENAI_CHAT
model: gpt-4o-mini
apiKey: "${secrets.OPENAI_API_KEY}"
systemPrompt: "You are an e-commerce operations lead. Do not exaggerate numbers; report facts concisely."
userPrompt: |
Summarize the order data below in three lines.
Always include total revenue and order count, and add one line if anything stands out.
Data: ${nodes.aggregate.response.body.summary}
Output¶
| Field | Description |
|---|---|
${nodes.summarize.response.body.content} |
The text the AI wrote |
${nodes.summarize.response.body.tokenUsage.totalTokens} |
Tokens used |
${nodes.summarize.response.body.tokenUsage.promptTokens} |
Input tokens |
${nodes.summarize.response.body.tokenUsage.completionTokens} |
Output tokens |
Careful with
temperature: some models, including recent Claude models, reject this value with a 400 error. If you do not need it, leave it out entirely.You pay for usage. eeumsae only makes the call on your behalf with your API key; the bill comes from OpenAI, Anthropic and the rest directly. If tokens worry you, cap them with
maxTokens.Want to branch on the AI's answer? → the
containsoperator in 06. Flow Control
transform_jmespath¶
Reshapes JSON data. Pull out only the fields you need, keep only what matches a condition, count things, and so on.
It uses JMESPath syntax.
Input¶
| Field | Required | Description |
|---|---|---|
expression |
✅ | A JMESPath expression |
data |
What to transform. If omitted, everything in input except expression |
⚠️ The result must be an object¶
A CALL node's output has to be a JSON object. Emitting a bare array or number fails. Wrap it in braces.
expression: "items[*].id" # ❌ array → fails
expression: "{ids: items[*].id}" # ✅ object → works
expression: "length(items)" # ❌ number → fails
expression: "{count: length(items)}" # ✅ works
Common patterns¶
# pull specific fields into an array
expression: "{ids: data[*].orderId}"
# filter by a condition (mind the backticks)
expression: "{active: items[?status == `active`]}"
# count and sum
expression: "{count: length(orders), total: sum(orders[*].amount)}"
# rename fields
expression: "{name: customer.name, amount: payment.total}"
# several at once
expression: "{count: length(orders), revenue: sum(orders[*].amount), firstOrder: orders[0]}"
Example¶
- id: aggregate
name: Aggregate orders
type: CALL
integration: transform_jmespath
input:
expression: "{count: length(@), totalRevenue: sum([*].amount)}"
data: "${nodes.fetch-orders.response.body.data | raw}"
Do not forget
| raw. Without that marker, an array or object passed to another node gets converted into a string. → 05. Connecting Data with Expressions
Output¶
The result of evaluating expression, as-is.
dataset¶
Reads and writes rows in a workspace dataset. Use it when you need to remember a value between executions (for example: order numbers you already processed, or a running total).
The dataset has to exist first. → 09. Datasets
Input¶
| Field | Required | Description |
|---|---|---|
datasetTitle |
✅ | The title of the target dataset (unique within the workspace) |
operation |
✅ | QUERY · INSERT · UPDATE · DELETE |
data |
INSERT/UPDATE | The values to store. Keys that are not dataset columns are dropped |
rowId |
UPDATE/DELETE | The id of the target row |
limit |
Maximum rows for QUERY. Default 100, maximum 1000 | |
offset |
Where QUERY starts |
Example¶
# read
- id: load-log
type: CALL
integration: dataset
input:
datasetTitle: "processing-log"
operation: QUERY
limit: 50
# write
- id: save-log
type: CALL
integration: dataset
input:
datasetTitle: "processing-log"
operation: INSERT
data:
orderId: "${nodes.trigger.response.body.orderId}"
processedAt: "${nodes.trigger.response.body.windowEnd}"
Output¶
| operation | Output shape | Reference example |
|---|---|---|
QUERY |
{ rows: [ {id, ...columns}, ... ] } |
${nodes.load-log.response.body.rows \| raw} |
INSERT / UPDATE |
{ id, ...columns } |
${nodes.save-log.response.body.id} |
DELETE |
{ deleted: true, id } |
${nodes.del.response.body.deleted} |
slack_post_message¶
Posts a message to a channel in a connected Slack workspace.
Before you start¶
- Connect Slack on the Connections screen. → 07. Managing Connections
- Invite the eeumsae bot into the channel you want to post to. In that channel, run
/invite @eeumsae. Skip this step and you get anot_in_channelerror.
Input¶
| Field | Required | Description |
|---|---|---|
channel |
✅ | Channel ID (for example C0123ABCDEF) or channel name |
text |
✅ | The message body |
In the visual editor you can pick channel from the channel list of the connected Slack.
Example¶
- id: notify
name: Send to Slack
type: CALL
integration: slack_post_message
input:
channel: "C0123ABCDEF"
text: |
📊 Today's sales summary
${nodes.summarize.response.body.content}
Output¶
| Field | Description |
|---|---|
${nodes.notify.response.body.ok} |
Whether it succeeded |
${nodes.notify.response.body.channel} |
The channel it was sent to |
${nodes.notify.response.body.ts} |
The message timestamp |
⚠️ This integration really does send¶
slack_post_message is marked as an integration with a side effect.
When you run it from an AI assistant over MCP, a workflow like this does not run straight away — it asks for approval once.
That safeguard exists so an AI cannot accidentally blast messages at your customers.
kakao_send_memo¶
Sends a notification to the "chat with yourself" room of the connected KakaoTalk account. If you do not use Slack, this is the simplest way to get alerts straight to your phone.
Before you start¶
Connect KakaoTalk on the Connections screen. → 07. Managing Connections
Input¶
| Field | Required | Description |
|---|---|---|
text |
✅ | The message body. 200 characters maximum |
Example¶
- id: notify-me
name: KakaoTalk alert
type: CALL
integration: kakao_send_memo
input:
text: |
[Revenue report]
${nodes.summarize.response.body.content}
Output¶
| Field | Description |
|---|---|
${nodes.notify-me.response.body.resultCode} |
Kakao's response code (0 on success) |
⚠️ There is exactly one recipient: you¶
The Kakao memo API has no recipient parameter. The message goes only to the KakaoTalk account you connected. This is for notifying one operator, not a team. Use Slack if several people need it.
The 200-character limit is enforced by the schema. Going over rejects the save or the run. When sending an AI summary, put "in 200 characters or fewer" in the
systemPrompt.
GitHub integrations¶
Work with GitHub Actions and pull requests. Use them to poll CI and deploy results and alert on them, re-run failed jobs automatically, or remind people about pull requests waiting for review.
Before you start¶
Connect GitHub on the Connections screen. All seven GitHub integrations share that one connection. → 07. Managing Connections
Shared input¶
Every GitHub integration takes owner and repo.
| Field | Description |
|---|---|
owner |
The repository owner (user or organization). Example: boboc-app |
repo |
The repository name. Example: weaver |
Reading¶
| id | Extra input | Output |
|---|---|---|
github_list_workflow_runs |
status (default completed) · perPage (default 20, max 100) |
runs (array) · totalCount |
github_get_workflow_run |
runId ✅ |
id · name · status · conclusion · htmlUrl · headBranch · headSha · runStartedAt |
github_list_run_jobs |
runId ✅ · filter (latest default / all) · perPage (default 30) |
jobs (array) · totalCount |
github_list_pull_requests |
state (open default) · sort (updated default) · direction (desc default) · perPage |
pullRequests (array) |
Writing ⚠️¶
| id | Extra input | What it does |
|---|---|---|
github_rerun_failed_jobs |
runId ✅ |
Re-runs only the failed jobs |
github_dispatch_workflow |
workflowFileName ✅ · ref ✅ |
Runs a workflow manually via workflow_dispatch |
github_create_issue_comment |
issueNumber ✅ · body ✅ |
Comments on an issue or PR (output: id · htmlUrl) |
github_dispatch_workflow requires the target workflow to declare a workflow_dispatch trigger.
Example — alert on failed CI¶
- id: runs
name: Fetch recent CI runs
type: CALL
integration: github_list_workflow_runs
timeout: 10s
input:
owner: "${vars.GH_OWNER}"
repo: "${vars.GH_REPO}"
status: "failure"
perPage: 5
- id: summarize
name: Summarize failures
type: CALL
integration: transform_jmespath
input:
expression: "{count: length(runs), latest: runs[0].name}"
data: "${nodes.runs.response.body | raw}"
Example — re-run failed jobs¶
- id: rerun
name: Re-run failed jobs
type: CALL
integration: github_rerun_failed_jobs
input:
owner: "${vars.GH_OWNER}"
repo: "${vars.GH_REPO}"
runId: "${nodes.trigger.response.body.workflow_run.id}"
These pair well with GitHub webhooks. Point a repository webhook at your eeumsae workflow and every
workflow_runevent hands you therunIddirectly. The signature header GitHub sends is recognized as-is. → 03. Setting Up Triggers
Options every CALL node can use¶
These can be attached to any CALL node, whatever its integration.
- id: fetch
type: CALL
integration: http_request
timeout: 10s # over this and it counts as a failure
retry-policy: # try again on failure
max-attempts: 3
backoff:
type: EXPONENTIAL
initial-delay: 500ms
retry-on: ["429", "5xx", "connect-error"]
input:
...
⚠️ retry-on filters HTTP status and timeout / connection errors on two separate axes.
Writing only retry-on: ["5xx"] means timeouts are still retried.
For the details → 10. Executions and Monitoring