Flowlith / DocsDashboard

External APIs and tools

Connect Iris to APIs, webhooks and approved MCP tools, with examples you can safely share.

On this page

Connect Iris to the systems your business already uses: check product availability, look up an order, save a lead or request a booking. An Action is one task you approve. A system connection is the API or MCP server that task uses; several Actions can share it.

This is different from the Website API, which lets your own website send customer messages to Iris. Actions let Iris call your business systems during a conversation.

Before writing an Action, check CRM, ERP and HIS backend requirements. If you are using MCP, start with How MCP connects to distinguish owner setup from customer tools. For examples of what each connection enables, read What Iris can do.

Choose your connection

Your system Use What Iris sends
An existing HTTP API API connection Your API's method, path, headers and JSON or form body
An automation webhook API connection The fields your workflow expects, without an extra Iris envelope
A token-authenticated MCP server MCP connection The arguments of one explicitly approved tool
A custom signed Iris webhook Advanced receiver The signed Iris request envelope described below

Use a final, publicly reachable HTTPS address on port 443. Redirects are not followed. API connections can have a fixed prefix, such as https://api.example.com/v1; all action paths stay below it. Credentials do not belong in URLs.

External systems that only support interactive OAuth/sign-in are not supported by these Actions connections. Use a compatible API-key integration or an automation webhook instead. Signing in to the Flowlith connector gives your AI app access to your Flowlith workspace; it does not sign Iris into every external service.

Start from your workspace

  1. Open Actions in your workspace. Describe one task, choose your AI app and select Copy prompt.
  2. Add the Flowlith connector using the displayed setup guide and finish browser sign-in. Its MCP endpoint is https://mcp.flowlith.com/mcp.
  3. Ask the assistant to list your businesses, select the correct one and inspect the existing systems before adding anything.
  4. Share the system's documentation, OpenAPI/Swagger document or a sample request with all credentials removed. Explain which fields customers provide and what the result should contain.
  5. Save a draft. Review its destination, inputs, field placements, customer checks, returned fields and whether it changes data.
  6. Give explicit permission before a real Test. After a successful Test, separately approve the exact version before turning it on for customers.

You can copy this starting brief:

text
Help me connect Iris to my business system, one task at a time.
First list my businesses and existing systems, then ask which one to use.
Task: tell customers whether a product is available.
API documentation: https://api.example.com/docs
Use only a read-only stock lookup for this first action.
Never ask for keys in chat or put them in tool arguments.
Use get_key_link so I can enter credentials on the secure page.
Save a draft and read its Preview back to me.
Ask before Test. Do not turn it on until I approve the tested version.

The URLs and sample records in this guide are illustrative. Replace them with your own system's documented contract; they are not Flowlith-provided services.

Add credentials privately

For each required key, ask the assistant to use get_key_link. Check the business, system and destination on the secure page before entering anything. The link works once, expires after 15 minutes and can be opened on a phone or computer. Do not share it; it authorizes the key save. Ask for a fresh link if it expires or the system configuration changes.

Enter credentials only on that secure page or in the dashboard's dedicated access-key field. Never put them in chat, example JSON, action instructions, Info, Memory or public code. If you already shared a key, revoke or rotate it with its provider; deleting the message is not enough. After saving, the assistant must reload the system's current version before continuing.

API authentication What to select What to enter privately
No authentication None Nothing; use only if your endpoint is intentionally accessible without a key
Bearer token Bearer The token only, without the Bearer prefix
Named header Header and its name, such as X-Api-Key The header value
HTTP Basic Basic username:password, not an already encoded value
Named query key Query and the parameter name The value, never a key-bearing URL

MCP supports None, Bearer and a named token header. Up to four additional fixed headers are supported; mark every credential header as secret and enter its value privately. Prefer narrowly scoped credentials. Connection details and key fingerprints can be reviewed later; key values are not shown again.

Direct API example: product availability

Suppose your inventory API has this read-only endpoint:

http
GET /v1/products/MUG-001/availability HTTP/1.1
Host: api.example.com
Accept: application/json

It returns:

json
{
  "sku": "MUG-001",
  "available": true,
  "quantity": 12
}

System configuration

This is an example add_system configuration, not a credential upload. If you have multiple businesses, use the business selected from list_businesses.

json
{
  "name": "Inventory",
  "kind": "api",
  "base_url": "https://api.example.com/v1",
  "auth": { "kind": "bearer" },
  "headers": []
}

Save the key using the private flow above. Then reload the system. Use its actual connection ID and current version in the draft below; the example UUID and version are placeholders.

Action draft

The following is a complete example of the non-secret arguments for save_action_draft. It deliberately has no customer binding because product availability is public business information, not a customer's private record.

json
{
  "name": "product_availability",
  "display_label": "Check product availability",
  "hard": {
    "mode": "direct",
    "connection_id": "11111111-1111-4111-8111-111111111111",
    "connection_version": 2,
    "method": "GET",
    "input_schema": {
      "type": "object",
      "additionalProperties": false,
      "properties": {
        "sku": { "type": "string", "maxLength": 40, "format": "sku" }
      },
      "required": ["sku"]
    },
    "fixed_fields": {},
    "system_fields": {},
    "requires_confirmation": false,
    "bind_customer_path": "",
    "link_hosts": [],
    "timeout_ms": 10000,
    "idempotent_retry": false,
    "direct": {
      "path": "/products/{sku}/availability",
      "body_type": "none",
      "sign": false,
      "idempotency_header": "",
      "placements": [
        { "source": "input", "field": "sku", "in": "path", "target": "sku" }
      ]
    }
  },
  "soft": {
    "when_to_use": "When a customer asks whether a product is available.",
    "synonyms": ["stock", "availability"],
    "field_hints": {
      "sku": { "hint": "Ask which product to check.", "examples": ["MUG-001"] }
    },
    "response_paths": ["sku", "available", "quantity"]
  }
}

For input {"sku":"MUG-001"}, Iris calls https://api.example.com/v1/products/MUG-001/availability with the privately configured authentication. It does not send a JSON body or add an Iris action envelope. This example's read-only setting is appropriate only if the endpoint really cannot change data. An HTTP method or a tool description alone is not proof.

Mapping other APIs and webhooks

  • Path: place a required input into a whole path segment, such as /orders/{order_ref}. It is a value, never a replacement hostname or URL.
  • Query: target a fixed parameter name, such as status. Optional missing inputs are omitted.
  • Header: target a permitted business header. Authentication belongs to the connection, not to a customer-controlled placement.
  • JSON body: use a pointer such as /customer/name to produce {"customer":{"name":"Sample Customer"}}. Inputs can remain flat even when the outgoing JSON is nested.
  • Form body: use body_type: "form" and fixed field names, such as email. Iris encodes the form for you.
  • Complete webhook URL: save the full address as the connection base, then use an empty action path "". A path of "/" is different: it adds a trailing slash.

Each input, fixed constant and mapped system field needs exactly one placement. Fixed constants are for non-secret business values. Do not put tokens in constants or plain headers. Direct requests have no Iris envelope; optional signing must be deliberately configured if your receiver needs it.

Customer identity and private records

A customer typing an order reference, email address or phone number does not prove that the record belongs to them. Your system must authorize access to private records. Configure Bind result to this customer where appropriate and return the ownership field at the configured customer-binding path, even if that field is not shown in Iris's reply.

Use trusted channel identity rather than a phone number supplied in conversation. A verified customer phone is available for WhatsApp only; Instagram, Messenger and website chat do not automatically acquire one. A phone-dependent Action is not available on those channels. Do not substitute a guessed phone or merge identities merely because the customer uses the same name.

Return only the fields Iris needs. For example, return delivery status and an estimated date rather than an entire customer account, payment record or internal staff note. The product-availability example above must not be copied unchanged for private order lookup.

Writes, Test and customer approval

Mark an Action changes data if it creates, changes, cancels or deletes anything. Examples include saving a lead, booking an appointment or cancelling an order. This is the default for MCP tools unless you explicitly verify that a tool is read-only.

A customer write follows this sequence:

text
Customer: Please book the Tuesday consultation at 10am.
Iris: Shall I book the Tuesday consultation at 10am?
Customer, in a later message: Yes, book that time.
Iris: Sends the approved request once, then reports the result.

Approval applies to the exact proposed details. Changed details need a new proposal. A reply from your external system is data, not customer consent. Iris does not automatically repeat a sent write, including after a timeout or a rejected request. An error may still mean your system changed data: inspect that system before deciding what to do next.

For a direct API that supports idempotency, configure its accepted header name in idempotency_header. Iris sends an operation identifier; your API must enforce deduplication. A header by itself does not prevent duplicates. If you build a receiver, commit the duplicate check and business change together in your own database.

Review → Test → turn on

  • Preview: read the exact saved version, destination, mappings, returned fields and customer checks. Saving a draft does not activate it.
  • Test: use invented data and a disposable test account whenever possible. A Test can change real data. Direct API and MCP calls have no automatic dry-run wrapper. Give explicit permission for a data-changing Test and verify the result in the external system. A successful response is not independent proof of the side effect.
  • Turn on: after the successful Test, answer “Should Iris start using this with customers?” with a clear yes to that exact version. The latest Test of the exact current version must succeed and be within seven days. Editing it means reviewing, testing and approving it again.
  • Customer preview: it can reach real connected systems. Writes are off by default and need separate enablement plus the customer's later-message confirmation; it is not a sandbox for your external account.
  • Stop: turn off one Action or choose Turn everything off. This stops new calls, not a request already sent.

Test can run while customer Actions are off. Turning on an approved Action enables it for the business without approving other drafts. Your business and customer channel must also be live; an Action marked On does not prove end-to-end message delivery.

Connect an MCP tool

There are two different MCP connections:

  1. Your AI app → Flowlith: the owner connector at https://mcp.flowlith.com/mcp, used to set up your business.
  2. Iris → your tool server: an external Streamable HTTP MCP endpoint that exposes customer tasks you approve.

For the second connection, use kind: "mcp" and the server's final endpoint, for example https://tools.example.com/mcp. Configure supported token authentication privately, then ask the assistant to run list_system_tools. Select a specific supported tool; do not grant access to the whole server.

A small tool input shape can look like this:

json
{
  "type": "object",
  "additionalProperties": false,
  "properties": {
    "order_ref": { "type": "string", "maxLength": 40 }
  },
  "required": ["order_ref"]
}

Use the exact tool name, current definition hash and supported schema returned by discovery; never invent a hash. If a tool is marked unsupported, simplify its interface or expose a narrow API/webhook instead. Nested inputs, references, unions and setup/discovery meta-tools cannot be selected as customer Actions. A server's read-only annotation is a hint, not your authorization.

Iris checks that the approved tool definition is still current before calling it. A changed or removed tool pauses affected Actions for review. Refresh the list, review the change, re-save, Test and approve again. Definition checks cannot prove that a remote service is truthful about its behavior.

Advanced: signed Iris receiver

Choose Advanced for a custom receiver that implements Iris's signed contract. This is different from a normal direct API. A receiver gets a signing secret through the dedicated owner flow; store it privately at the receiver, never in chat or frontend code. Keep authentication credentials separate from the signing secret.

A non-GET receiver request includes these headers:

http
Content-Type: application/json
Iris-Delivery-Id: <delivery UUID>
Iris-Timestamp: <Unix seconds>
Iris-Action: order_status
Iris-Signature: v1=<HMAC-SHA256 hex>
Idempotency-Key: <operation identifier>

For a read-only order_status Action with one input and no mapped system fields, the JSON body has this shape. This illustrates the envelope only: a private-order lookup also needs your customer-ownership checks and appropriate trusted identity mapping. Displayed formatting is explanatory; verify the raw bytes you actually receive.

json
{
  "action": "order_status",
  "version": 1,
  "order_ref": "DEMO-1234",
  "input": { "order_ref": "DEMO-1234" },
  "fixed": {},
  "context": { "mode": "live" }
}

Input, fixed and mapped trusted fields also appear at the top level. They must not collide with envelope names. The context includes the mode (live, preview or test) and only mapped trusted values. Confirmed customer writes also carry confirmation with id and confirmed_at; reads and owner Tests do not. A receiver should keep test mode non-destructive. Mode is not an ownership check.

For GET, the body is empty: input/fixed/mapped values are query parameters, with context encoded as JSON. Query keys are sorted and spaces use %20. Keep GET receivers side-effect-free.

Verify the signature

The signing input is:

text
v1:<timestamp>:<deliveryId>:<METHOD>:<exactPublicPathAndQuery>:<SHA256(rawBodyBytes) as lowercase hex>

Calculate HMAC-SHA256 with the signing secret and compare its lowercase hex digest with a v1= signature using a constant-time comparison. Verify the timestamp is within five minutes. During signing-secret rotation, accept either valid signature while both are supplied.

The following Node.js helper verifies the signature only. Pass a Buffer containing the original raw body, before parsing JSON. Behind a reverse proxy, req.url must be the exact public encoded path and query, supplied by trusted routing configuration rather than an untrusted forwarded header.

javascript
import {createHash,createHmac,timingSafeEqual} from 'node:crypto';

/** Signature only. The receiver must atomically dedupe delivery/write IDs before side effects. */
export function verifyIris(req, rawBody, secrets, now = Date.now()/1000) {
  if(!req||!req.headers||typeof req.headers!=='object')return false;
  const headers=Object.fromEntries(Object.entries(req.headers).map(([k,v])=>[k.toLowerCase(),v]));
  const ts=headers['iris-timestamp'],id=headers['iris-delivery-id'],signature=headers['iris-signature'];
  if(typeof ts!=='string'||!/^\d{1,12}$/.test(ts)||!Number.isFinite(now)||Math.abs(now-Number(ts))>300
    ||typeof id!=='string'||! /^[a-f0-9]{8}-[a-f0-9]{4}-4[a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i.test(id)
    ||typeof signature!=='string'||signature.length>140||!['GET','POST','PUT','PATCH','DELETE'].includes(req.method)
    ||typeof req.url!=='string'||!req.url.startsWith('/')||req.url.includes('#')||!Buffer.isBuffer(rawBody))return false;
  const base=`v1:${ts}:${id}:${req.method}:${req.url}:${createHash('sha256').update(rawBody).digest('hex')}`;
  return (Array.isArray(secrets)?secrets:[secrets]).some(secret=>{
    if(typeof secret!=='string'||!secret)return false;
    const want=createHmac('sha256',secret).update(base).digest();
    return signature.split(',').some(part=>{
      const m=part.trim().match(/^v1=([a-f0-9]{64})$/);if(!m)return false;
      const got=Buffer.from(m[1],'hex');return timingSafeEqual(got,want);
    });
  });
}

After verification, validate the business inputs and customer ownership. Atomically deduplicate delivery IDs for at least ten minutes before processing. For writes, also persist the signed confirmation.id, compare it with Idempotency-Key, and commit deduplication with the business update. Do not use an in-memory set as the production ledger. A valid signature does not make customer input safe for SQL, shell commands or arbitrary record access.

Receiver responses

Return application/json within the Action's timeout (at most ten seconds), with no more than 64 KiB after decompression. Small plain-text responses up to 2 KiB are accepted, but JSON gives you explicit result fields.

json
{
  "status": "shipped",
  "estimated_delivery": "2030-01-04"
}

Use meaningful HTTP status codes: 2xx for success, 400/422 for invalid input, 401/403 for access problems, 404 for a missing record, 409 for a conflict, 429 for busy and 5xx for failure. Return a truthful, minimal result—not instructions, credentials or whole customer records. Links must use hosts approved in the Action, and private results must include the configured ownership field.

Only send Iris-Signature-Verified: 1 after actually verifying the signature. This lets the owner Test display your receiver's acknowledgement; it is not an independent audit of your implementation. Keep request bodies and credentials out of ordinary automation logs.

Supported inputs and maintenance

  • Up to 12 flat input fields: strings, bounded numbers/integers, booleans or arrays of strings.
  • Strings need an explicit maximum length of 1–500 characters. Arrays need a maximum of 1–20 items, with bounded strings inside.
  • Numbers need explicit minimum and maximum values. Required fields must be listed. Unknown fields are rejected.
  • Select only the response paths Iris should use. Large or deeply nested results are not suitable as conversation context.
  • Updating a connection or rotating its key stops linked Actions until they are saved against the new version, reviewed, tested and approved again. Configuration edits other than a name-only change clear saved key slots; re-enter required keys privately.
  • Removing an Action does not delete its shared system connection or upstream records. Removing a system stops its linked Actions but does not revoke the key at its provider. Revoke it there when it is no longer needed.

Actions do not add a separate Flowlith message-credit charge; normal reply charging still applies. Your external provider may charge for its own requests.

If setup fails, check Troubleshooting. For how Actions fit into a conversation, read How Iris works; for connecting a customer channel, start with WhatsApp or Website integration.

Continue readingConnect WhatsApp