Skip to content

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_jmespath you 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 contains operator 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.

${nodes.aggregate.response.body.count}
${nodes.aggregate.response.body.totalRevenue}

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

  1. Connect Slack on the Connections screen. → 07. Managing Connections
  2. Invite the eeumsae bot into the channel you want to post to. In that channel, run /invite @eeumsae. Skip this step and you get a not_in_channel error.

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_run event hands you the runId directly. 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


Next → 05. Connecting Data with Expressions