# Flowlith public documentation

Public integration and product guidance only. Never share credentials with an AI assistant.

Source: https://docs.flowlith.com/

# Start with Iris

Flowlith helps you set up and manage **Iris**, the assistant that answers your business’s customer conversations. These guides are public: share them with your developer or AI assistant without sharing your account or credentials.

## What are you connecting?

- **An API, automation or tool:** follow [External APIs and Actions](https://docs.flowlith.com/actions).
- **Your website:** use the [website embed example](https://docs.flowlith.com/website-embed) and [Website API reference](https://docs.flowlith.com/website).
- **A WhatsApp number:** follow [WhatsApp onboarding](https://docs.flowlith.com/whatsapp).
- **Instagram or Messenger:** follow [Customer channels](https://docs.flowlith.com/channels).
- **An AI app or your own MCP tools:** follow [How MCP connects](https://docs.flowlith.com/mcp).

Start with [What Iris can do](https://docs.flowlith.com/capabilities) to distinguish built-in help from tasks that need an integration. For a CRM, ERP or hospital information system, use the [backend requirements checklist](https://docs.flowlith.com/backend-integration).

These are different connections. Adding Flowlith to an AI app does not connect your WhatsApp number. Creating a website API key does not install a chat widget. Saving an external-system key does not turn on an Action.

## Before you begin

Have an email address you can verify, your business details, and permission to connect the accounts you plan to use. Prepare opening hours, service areas, policies, and the situations where your team should take over. You do not need a Meta account for a website-only integration.

Use a desktop or laptop for the dashboard; narrower screens currently show **Open on desktop**. These public guides and secure one-time key-entry links can also be opened on a phone.

Never paste passwords, login codes, API keys, payment details or customer records into these examples or an AI chat. Use the provider’s sign-in screen and Flowlith’s dedicated key-entry fields.

## Set up your business

1. Open [Flowlith onboarding](https://onboard.flowlith.com/join), answer the business questions, choose your intended channels, and verify your account email. Review the Terms and Privacy Policy before submitting.
2. Open the [dashboard](https://dashboard.flowlith.com/profile). Select the correct business if you manage more than one.
3. Check the review status on **Home**. New businesses need Flowlith approval before Iris can launch. You can prepare your business information while review is pending.
4. Open **About business** (called **Info** in some AI tools), review the onboarding draft, and select **Save changes**. A nonempty saved note is required; an unsaved draft is not enough. [Writing business information](https://docs.flowlith.com/info)
5. Optionally add shared facts in **Memory**, and procedures or reference material in **Actions**. Memory is not required for the first launch. [Memory guide](https://docs.flowlith.com/memory)
6. Connect the channels you want to use. Meta sign-in happens in your browser. For a website, create an API key and finish the server-side integration.
7. Once approved and your business details are saved, return to **Home** and select **Launch Iris**. Saving details or connecting a channel does not launch Iris automatically.
8. Read the launch status, then test a customer question. Use fictional information and test access if external Actions are connected. An answered customer preview uses one credit and can change real data through connected tools.

## Verify the whole journey

Do not stop at a green connection label. Verify the result you actually need:

| Check | What it tells you |
| --- | --- |
| Account approved | Your business can proceed with launch. |
| Channel connected | Connection setup was accepted; it is not a delivery test. |
| Iris launched | Iris has been launched; this is not a continuous health indicator. |
| Teaching applied | Saved assets and skills are in use, rather than only a draft. |
| Customer preview answered | The preview worked, not necessarily your Meta channel or website. |
| Message sent from the real channel and reply received | That specific end-to-end customer path worked. |

Try a follow-up in the same conversation to check continuity. Test each connected channel separately. An Instagram test does not prove WhatsApp delivery, and a dashboard preview does not prove your website’s server integration.

## Copy this brief for your developer or AI assistant

Replace the bracketed fields with non-secret information. Share the public docs, not an exported private workspace.

```text
Help me integrate Flowlith / Iris for [business name].

Customer channels: [WhatsApp / Instagram / Messenger / website]
Website stack, if relevant: [framework and backend]
External systems: [names and public documentation links]
First customer journey: [for example, check stock or request a booking]
Changes requiring approval: [for example, booking or cancellation]

Start with these public guides:
https://docs.flowlith.com/how-iris-works
https://docs.flowlith.com/actions
https://docs.flowlith.com/website
https://docs.flowlith.com/website-embed
https://docs.flowlith.com/whatsapp

Do not ask me to paste secret keys, login codes or customer records.
Keep website API credentials on my server, never in browser code.
Use browser authorization or the secure key-entry form for credentials.
Explain any real-world effect before testing. Ask for my approval before
launching, applying changes, running paid previews or enabling Actions.
Distinguish a saved draft, a successful test and a live integration.
If you cannot access the connected workspace, say so instead of guessing.
```

Read [How Iris works](https://docs.flowlith.com/how-iris-works) for conversation, learning and credit principles. For a setup that is not completing, use [Troubleshooting](https://docs.flowlith.com/troubleshooting).


---

Source: https://docs.flowlith.com/capabilities

# What Iris can do

Iris is a customer-facing sales and service assistant. She can explain your business, ask useful follow-up questions, and use the **specific external operations you have connected and enabled**. She is not a replacement for your CRM, ERP or hospital information system, and adding an API key does not grant her every capability in that system.

## Three kinds of capability

| Capability | What is available | What you must provide |
| --- | --- | --- |
| Conversation and business knowledge | Answer questions, explain policies, collect details and follow taught procedures | An approved, launched business, saved business information, useful references and a connected customer channel |
| Live business-system work | Look up current records or perform an approved operation | A compatible API, webhook or MCP tool; scoped credentials; backend authorization; a tested, active Action |
| A feature your backend does not offer | Not created automatically by Iris | Your developer or vendor must implement the missing operation, or your team must handle it |

The integrations below are **examples of what can be built**, not claims that a named CRM, ERP or HIS is already connected. See [Backend requirements](https://docs.flowlith.com/backend-integration) for the interface your provider must supply.

## Conversation and knowledge

With the appropriate business setup, Iris can:

- Answer customer questions from approved business facts, FAQs and reference material.
- Explain products, service areas, opening hours, policies and next steps without needing access to your operational system for every answer.
- Ask follow-up questions, gather the information needed for a task, and continue the same customer conversation.
- Follow taught procedures and your preferred tone and language. Test the languages and terminology your customers actually use; accuracy is not guaranteed.
- Use shared business notes and learn reusable lessons. Shared knowledge is not a live stock database, a customer directory or proof of an external transaction.
- Understand supported incoming photos and voice notes. Customer document reading is availability-dependent. Supported formats and channel differences are in [Channels and media](https://docs.flowlith.com/channels).

These are customer conversations on connected WhatsApp, Instagram, Messenger or your website integration. The dashboard's customer preview is a separate test conversation using applied knowledge; it is not an administrative chat or a view of real customers' conversations.

## What a CRM can enable

A **CRM** manages customer relationships. Connect narrow tasks rather than exposing the whole account:

| Example customer task | What Iris could do after integration | Backend responsibility |
| --- | --- | --- |
| “Can your team contact me about this service?” | Gather the agreed details and submit a lead after confirmation | Validate the fields, deduplicate the lead and return a reference |
| “I need help with my order.” | Create a support ticket with the agreed summary | Attach it to the correct authorized customer and return a ticket ID |
| “Has my support request been resolved?” | Read the current ticket status | Verify customer ownership before returning only the permitted status |
| “Please update my delivery contact.” | Propose and submit an allowed contact change | Verify the customer, limit editable fields and record the change |

Remembering a name in a conversation does not create a CRM contact. Saying “the team will follow up” does not create a ticket or notify staff. Those outcomes need a working Action and a confirmed result.

## What an ERP can enable

An **ERP** manages operational records such as inventory, orders and billing. Possible connected tasks include:

- Check current stock, product availability or a customer-visible price from the authoritative source.
- Retrieve an authorized customer's order, delivery or invoice status.
- Submit a reservation, service request or permitted order change after the customer confirms the details.
- Give a customer an approved payment link returned by their business system, without handling card details in chat.

Your ERP must calculate totals, tax, discounts, stock reservations and eligibility itself. A customer message, uploaded invoice or screenshot is not proof of payment. Do not ask Iris to infer a successful transaction from those materials.

Actions are short request/response operations, not a background workflow engine. For a slow job, your backend should return an operation reference and an explicit pending status, then offer a separate read-only status check. “Accepted” or “queued” is not “completed.”

A stock lookup alone does not create a purchasing workflow. An invoice-status lookup alone does not collect a payment or issue a refund. Configure and test each operation separately; a multi-step business transaction should be coordinated by your backend.

## What a HIS can enable

Here **HIS** means a hospital information system. The same integration mechanism can support appropriate **non-clinical administrative workflows**, for example:

- Explain approved visiting hours, locations, administrative requirements and contact routes from public information.
- Check appointment availability through an approved scheduling endpoint.
- Request, reschedule or cancel an appointment through a tested operation, after identity checks and confirmation.
- Check a permitted administrative request's status or request staff assistance through an approved workflow.

These are integration possibilities, **not a built-in clinical connector**. Your organization must approve the use case and what data can be shared before connecting patient-related operations. Use fictional records for setup and testing.

The HIS or your integration backend must verify the person and their authority to access each record. A WhatsApp number, a typed patient ID, name or date of birth is not sufficient authorization by itself. Use your organization's approved secure verification flow outside ordinary chat. Never request login codes, passwords or full medical records as a workaround.

Return only the information needed for the approved administrative task. Do not put patient records in shared business knowledge. This guide does not establish a clinical, diagnostic, prescribing, emergency-response or compliance capability. Keep clinical decisions and urgent care with the appropriate professional service.

## A connected operation, end to end

This is an illustrative appointment request, not a ready-made integration. The backend must establish customer scope and enforce availability and booking rules first.

```text
Customer: Is there an appointment on Tuesday afternoon?
Iris: [Uses the approved availability Action.]
Backend result: Two eligible slots, with stable slot references.
Iris: [Proposes that exact slot with the booking Action; no booking is sent yet.]
Iris: There is a 3:00 pm slot. Would you like me to book it?
Customer: Yes, book the 3:00 pm slot.
Iris: [Confirms that same booking proposal through the approved Action.]
Backend result: A confirmed booking reference and time.
Iris: Your appointment is confirmed for 3:00 pm. Reference: DEMO-4821.
```

Only show the final confirmation if the system actually confirms it. If the request times out, Iris must not invent success or send another booking blindly. Check the original request in the destination system using your recovery workflow.

Customer confirmation is permission for the proposed change; it is **not** proof of identity or a replacement for backend authorization. A changed slot, amount, recipient or other material detail requires fresh confirmation.

## What connecting MCP changes

There are two separate connections:

1. **Your AI app → Flowlith:** an authenticated owner can ask the app to help manage business setup, teaching and Actions. This uses Flowlith browser sign-in.
2. **Iris → your external MCP tools:** customer conversations can use only the tools you have selected, reviewed, tested and activated as Actions. This uses the system's supported server credentials, not your AI app's Flowlith sign-in.

Connecting your AI app does not attach its other plugins, files or accounts to Iris. An owner management tool is not a customer-facing tool. Read [How MCP connects](https://docs.flowlith.com/mcp) for both flows and exact setup steps.

## What is not included automatically

- A universal, preconfigured connection to every CRM, ERP or HIS vendor. Compatibility must be checked against the actual API or tool contract.
- Direct database access, SQL execution, shell commands, browser automation or arbitrary web browsing in customer conversations. Do not expose broad administration or “run anything” tools through your backend either.
- Automatic access to every tool on a connected MCP server, or permission to install new customer tools from a chat message.
- Interactive OAuth login to external systems through customer Actions. Use the supported key/token connection or an approved integration backend.
- A hosted website widget: your website needs its own interface and secure backend integration.
- A built-in bulk campaign, scheduled-reminder or outbound-email engine. An explicitly approved Action may request a permitted workflow from a separate service that provides it; sending, scheduling and consent enforcement belong to that service.
- Voice calls, generated voice replies, image/video creation, or automatic outgoing file delivery as built-in chat features. Understanding an incoming voice note or photo is not the same as generating or sending media.
- Automatic cross-channel customer identity linking, patient verification, payment processing, or a staffed live-handoff inbox. Your authorized backend and team must supply those workflows where needed.
- Guaranteed correctness or successful third-party delivery. A connected badge, successful preview or polite reply is not proof of an external result.

## Define one useful first journey

Give your developer or AI assistant this non-secret brief. It makes the missing work visible before anyone promises that Iris can do it.

```text
System: [CRM / ERP / HIS / custom backend, name and version]
Customer task: [one specific outcome]
Channel: [WhatsApp / Instagram / Messenger / website]

Facts Iris may answer from our approved knowledge:
[public facts and reference material]

Read operation needed:
[name, documented endpoint or MCP tool, required inputs, allowed outputs]

Write operation needed, if any:
[name, exact effect, customer confirmation, confirmed result]

Identity and permission checks performed by our backend:
[how the current customer is verified and authorized for this record]

Test fixture and success evidence:
[fictional account, expected record/reference, who checks the destination]

Failure and human route:
[what Iris should say, how the original request is checked, who helps]

Never include credentials, real patient/customer records or login codes.
Do not claim a connector or capability is ready until this journey passes.
```

Next: [Backend requirements](https://docs.flowlith.com/backend-integration) → [MCP connections](https://docs.flowlith.com/mcp) or [API and Action examples](https://docs.flowlith.com/actions) → test the real customer channel separately.


---

Source: https://docs.flowlith.com/how-iris-works

# How Iris works

Iris answers customer messages using your business information, relevant conversation context, available reference material, and any Actions you have enabled. Manage that setup in Flowlith’s dashboard or through a connected AI app.

For concrete CRM, ERP and HIS examples and unsupported features, see [What Iris can do](https://docs.flowlith.com/capabilities). For the two different MCP connections, see [How MCP connects](https://docs.flowlith.com/mcp).

## A customer message, step by step

```text
Customer sends a message on a connected channel
  → Iris uses the business setup and that conversation’s context
  → Iris can consult relevant knowledge or an enabled Action
  → A data-changing Action needs the customer’s confirmation
  → The answer returns to the customer’s channel
```

Opening hours can come from your business information. Today’s stock needs an appropriate connected source; a note or an old chat is not proof of current availability. If an external action fails or its result is uncertain, do not treat a confident-sounding reply as proof that the external change happened. Check the connected system.

## One business, separate conversations

Your business’s channels share its business setup. Customer conversations remain separate by channel and customer identity. The same person on WhatsApp and Instagram is not automatically treated as the same customer, even if their name or phone number looks familiar.

For website chat, your backend must authenticate the visitor and supply a stable customer identifier. The business API key identifies your business, not the person using your website. Read the [Website API](https://docs.flowlith.com/website) before choosing an identity scheme.

A customer preview is a separate test conversation. It is not an inbox view and does not impersonate a real customer or give you access to their private customer-memory records.

## Put knowledge in the right place

| Place | Use it for | Do not use it for |
| --- | --- | --- |
| **About business / Info** | Business facts, hours, policies and preferred tone | Passwords, customer profiles or long process manuals |
| **Memory** | Short shared facts and useful lessons | Customer lists, individual orders or transcripts |
| **Actions → Assets** | Customer-safe Markdown references such as FAQs | Credentials, private records or executable programs |
| **Actions → Your skills** | When and how to follow a business procedure | Granting account access just by mentioning a service |
| **Actions — external connections** | Reviewed connections and approved external operations | Arbitrary access to every tool in an account |

A link describes a resource; it does not authorize account access. A skill describes a procedure; it does not itself create a working integration. Connect the external system and test the exact Action separately.

## Learning and owner edits are different

Iris can learn reusable shared notes and procedures over time. Review **Memory** regularly and keep important business rules clear in **About business** and approved materials. AI-generated lessons can be incomplete or mistaken; important facts still need verification against a reliable source.

Owner edits follow explicit save and apply steps:

- **About business** and **Memory** use **Save changes**. Saving replaces the complete note, not just the edited lines.
- **Assets and skills** are saved as drafts first. **Apply to Iris** publishes the whole saved set, including other pending edits for the business.
- **External Actions** have their own Preview, Test and exact-version activation flow. Applying teaching does not replace Action activation.

Do not assume every learned note waits for owner approval. The draft-and-Apply workflow describes your teaching edits, not every piece of learning Iris may perform.

If a save reports a conflict, preserve your edits, reload the latest version, merge deliberately, and save again. Pending or unconfirmed is not the same as applied. Review the displayed status before testing.

## Customer memory is not shared Memory

When customer memory is enabled for a business, Iris can retain useful customer-reported details for that customer’s channel conversation. This is separate from shared business Memory and is not a customer directory. Customer-reported details are not verified order or payment records.

Never put individual customer names, contact details, orders or transcripts into shared Info, Memory, assets or skills. Those materials are business-wide context. Clearing shared Memory does not erase conversation history or individual customer records. For privacy requests, use the appropriate customer/account workflow or contact support; deleting one note does not remove everything.

## Actions can change the outside world

An Action might read an order, create a lead or request a booking. The owner chooses what is available and reviews its setup before enabling it. For a data-changing customer Action, Iris proposes the change and asks for confirmation in a later customer message. Changed details require a fresh confirmation; “do whatever is needed” is not an exact approval.

**Owner Tests and customer previews are real.** A connected tool can write to the external system during a test. Prefer a test account or limited test key, fictional data and an operation you can safely undo. If a write’s result is uncertain, inspect the destination before deciding to retry. [Actions integration guide](https://docs.flowlith.com/actions)

## Message credits

One answered customer or customer-preview message uses **one credit**. It is not a token count. Reconnecting the same website message correctly does not create another charge for that same answered message. Follow the message-ID rules in the [Website API reference](https://docs.flowlith.com/website).

Credits belong to the selected business. Check its balance and available purchase options in **Billing**. Plan credits are used first and do not roll over; one-time credit packs do not expire. After credits run out, a 200-message grace allowance is available before new replies stop. Grace usage is deducted from the next purchase. Use the dashboard’s current balance rather than treating the allowance as extra purchased credits.

Customer document reading does not add a separate OCR credit charge to the normal answered-message credit. External services you connect can have their own usage charges. A test is not necessarily free or harmless: review its credit use and external effects before sending it.

## What Iris does not guarantee

Iris is an AI assistant, not a guarantee of factual accuracy, stock availability, payment completion or third-party delivery. Teach clear escalation rules, keep references current, and verify important changes in the system that owns them. Do not promise customers a live handoff, refund, booking or other result until you have configured and tested the process that makes it happen.

[Set up your business](https://docs.flowlith.com/) · [Manage your workspace](https://docs.flowlith.com/workspace) · [Troubleshoot a problem](https://docs.flowlith.com/troubleshooting)


---

Source: https://docs.flowlith.com/mcp

# How MCP connects

MCP is a way for an AI application to discover and call named tools. **Connecting an MCP server does not give Iris unrestricted access to a business system.** There are two different connections:

| Connection | Who uses it? | Authentication | Purpose |
| --- | --- | --- | --- |
| Your AI app → Flowlith | You, the business owner | Flowlith browser OAuth | Set up and manage Iris, teaching and approved Actions |
| Iris → your external MCP server | Iris during a customer conversation | A privately saved token/header, or no auth for deliberately public tools | Execute only the customer Actions you explicitly selected, tested and enabled |

The first connection does not automatically create the second. Your customer's WhatsApp account does not sign them into your CRM, ERP or HIS either. Your backend still owns record authorization. See the [backend readiness guide](https://docs.flowlith.com/backend-integration).

## 1. Connect your AI app to Flowlith

Use a client that supports remote MCP and browser OAuth. Add a connection named **Flowlith** with this public endpoint:

```text
https://mcp.flowlith.com/mcp
```

For clients with this JSON configuration format, the configuration contains no credentials:

```json
{
  "mcpServers": {
    "iris": { "url": "https://mcp.flowlith.com/mcp" }
  }
}
```

1. Add the remote connection or install the [public client package](https://github.com/jubsnfzm/iris-plugin#connect), using the instructions for your app. App plans and workspace policies may restrict custom connections.
2. Complete Flowlith sign-in in your browser: email, email code, then **Allow**. Never paste the code, a token or an Authorization header into chat or configuration.
3. Enable Flowlith in the conversation if your app requires it. Ask for this read-only check:

```text
Call list_businesses using Flowlith. Do not start onboarding or change anything.
If more than one business is returned, ask which one I want to manage.
If sign-in or tool access is missing, explain that instead of guessing.
```

An empty business list can be a valid signed-in result. A tool list or an installed skill is not proof of access: Flowlith allows discovery before sign-in, but business tools require authorization. An unsigned tool call asks the client to complete sign-in; it does not read your business.

| What you see | What it proves |
| --- | --- |
| Package installed | The client has the package, not necessarily a working connection. |
| Tools discovered | The server can describe its tools, not that you are authorized. |
| Browser sign-in completed | An account authorized the app; verify business access next. |
| Successful `list_businesses` | The app can access your authorized business list. |
| A successful customer preview | That preview worked; it does not prove external-system or channel delivery. |

The owner connection can help with onboarding, business information, teaching drafts/Apply, business status, approved launch, customer previews and external Action setup. It is not a customer inbox, transcript export or contact-list browser. Website API keys, Meta sign-in and account settings remain secure browser steps. Read [Iris capabilities](https://docs.flowlith.com/capabilities) for the full boundary.

## 2. Decide whether your backend needs MCP

**MCP is optional.** If the system already provides a suitable HTTPS API or automation webhook, use a direct API Action instead. A CRM/ERP/HIS does not need to install the Flowlith owner plugin.

Use external MCP when your team already runs a compatible tool server or wants to expose narrowly defined business operations. A database address, admin website, local command or vendor name is not an MCP endpoint. If your system only offers an interactive OAuth login, this external connection cannot use it yet; provide a supported token API, token MCP server or secure integration adapter. Do not reuse a personal session cookie.

## 3. Register your external MCP system

In your connected AI app, first call **list_businesses**, select the business, then **list_systems** to avoid duplicate setup. For multiple businesses, include the returned `business` identifier in setup calls.

The following are non-secret arguments for **add_system**. This is a fictional inventory server, not a hosted Flowlith integration:

```json
{
  "name": "Inventory tools",
  "kind": "mcp",
  "base_url": "https://inventory.example.com/mcp",
  "auth": { "kind": "bearer" },
  "headers": []
}
```

1. Save the system, then call **get_key_link** with the returned `connection_id` and required `slot` (normally `auth`). Enter the token only on that secure page. The single-save link expires after 15 minutes; do not share it.
2. Call **list_systems** again after saving a key. Use the current connection version, not the version from before the key save.
3. Call **list_system_tools** with that `connection_id` and `version`. This refreshes the tool menu and can mark changed Actions for review; it is not a customer tool execution.
4. Choose one task-specific tool with no refusal reason. Do not choose discovery, installation or tool-management tools. Review its meaning, inputs and actual effects; a `readOnlyHint` is not proof that it cannot write.
5. Save the Action draft, Preview, Test with permission, then separately approve activation. Detailed steps follow below.

External MCP supports **Bearer**, a named credential **header**, or **None** for an intentionally public server. Basic/query-key authentication and interactive OAuth are not supported for external MCP. Additional fixed headers are allowed; secret values still go through the private key-entry flow, never JSON examples or tool arguments.

## 4. What your MCP server must implement

Use one final, publicly reachable **HTTPS endpoint on port 443**, with valid TLS and no redirects. Iris sends JSON-RPC POST requests. It accepts `application/json` responses or JSON-RPC messages in `text/event-stream` response frames.

```text
Iris → initialize
Server → protocolVersion + capabilities.tools
Iris → notifications/initialized
Server → HTTP 202 or 204
Iris → tools/list (and any cursor pages)
Server → named tools and supported input schemas
Iris → tools/call for the selected, approved tool
Server → matching JSON-RPC result or error
```

Iris currently negotiates protocol versions **2025-11-25**, **2025-06-18** or **2025-03-26**. It proposes 2025-11-25. If initialization returns an `Mcp-Session-Id`, subsequent requests carry it along with `MCP-Protocol-Version`. Match each request's JSON-RPC ID in the response. Treat an initialized notification as a notification, not a request requiring a JSON-RPC result.

Do not require a local stdio process, a separate legacy GET/SSE endpoint, sampling, elicitation, or another server-initiated request. This connection calls tools; it does not automatically import your prompts, download resources, browse files, or install extra tools. Keep each response at most **64 KiB** and operations short enough for the configured Action timeout, at most **10 seconds**. That time budget includes initialization and tool discovery, not just your tool handler. A large reporting job should return a bounded status/result through a purpose-built endpoint, not block the chat.

### A compatible tool definition

Return this tool inside the `tools` array of your `tools/list` result. It checks public product availability, not a private customer record:

```json
{
  "name": "get_availability",
  "description": "Read current availability for a public product SKU.",
  "inputSchema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "sku": { "type": "string", "maxLength": 40 }
    },
    "required": ["sku"]
  },
  "annotations": { "readOnlyHint": true }
}
```

Use a flat object with at most 12 input fields: bounded strings, booleans, bounded numbers/integers, or arrays of up to 20 strings. Strings are limited to 500 characters. Nested input objects, unions, nullable types, references such as `$ref`, and arbitrary JSON Schema features are not supported. Expose a simpler task-specific tool if your backend has a more complex native schema. Do not make Iris supply credentials as tool arguments.

### The call and response

After setup and activation, the call for this example looks like this. The ID is illustrative; echo the actual request ID:

```json
{
  "jsonrpc": "2.0",
  "id": 4,
  "method": "tools/call",
  "params": {
    "name": "get_availability",
    "arguments": { "sku": "MUG-001" }
  }
}
```

A successful JSON response can contain both a short text summary and structured fields:

```json
{
  "jsonrpc": "2.0",
  "id": 4,
  "result": {
    "isError": false,
    "content": [{ "type": "text", "text": "MUG-001 is available." }],
    "structuredContent": { "sku": "MUG-001", "available": true, "quantity": 12 }
  }
}
```

The Action can select `text`, `structured.sku`, `structured.available` and `structured.quantity` as returned fields. Image/binary content is not forwarded as customer knowledge. Linked resources are not fetched automatically. Return only the fields needed for the task, not the whole account or record. `isError: true` is a tool failure, not a successful business operation. An HTTP 200 alone is not proof of success.

## 5. Turn one selected tool into an Action

Use **save_action_draft** with the exact tool name/hash returned by **list_system_tools**. The sample below uses a placeholder connection ID, version and hash; replace them with current returned values. It is read-only only because the example endpoint truly does not change data:

```json
{
  "name": "product_availability",
  "display_label": "Check product availability",
  "hard": {
    "mode": "mcp",
    "connection_id": "11111111-1111-4111-8111-111111111111",
    "connection_version": 2,
    "input_schema": {
      "type": "object", "additionalProperties": false,
      "properties": { "sku": { "type": "string", "maxLength": 40 } },
      "required": ["sku"]
    },
    "fixed_fields": {}, "system_fields": {},
    "requires_confirmation": false,
    "bind_customer_path": "", "link_hosts": [],
    "timeout_ms": 10000, "idempotent_retry": false,
    "mcp": {
      "tool_name": "get_availability",
      "tool_hash": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
    }
  },
  "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": ["text", "structured.sku", "structured.available", "structured.quantity"]
  }
}
```

This example intentionally has no private-record customer binding. **Do not copy that choice for private CRM, ERP or patient records.** Your backend must authorize the specific customer and the Action must apply the appropriate customer check. A customer-typed record number or phone number is not proof of identity. See [backend requirements](https://docs.flowlith.com/backend-integration).

1. **preview_action**: use the returned `action_id`. Read the destination, inputs, customer check, selected output, warnings and exact `revision` to the owner.
2. **test_action**: use that `action_id`, `revision` and fictional `input`. Test reaches the real system. Explain effects and obtain permission first; provide `real_write_ok: true` only after the requested permission. An MCP Test can require this flag because its transport uses POST even when the business operation is read-only.
3. Inspect the actual result. For writes, also verify the external record; never automatically repeat a sent write after a failed or uncertain response.
4. Ask whether Iris should use this exact version with customers. Only after a clear yes, call **turn_on_action** with the exact `action_id`, `revision` and `owner_said_yes: true`.

Activation requires a successful, recent Test of that exact version. Customer writes also require confirmation in a later customer turn. Owner Test permission and customer write confirmation are separate. A draft, discovered tool or saved key is never activation.

## 6. Keep the connection working

Changed or removed tool definitions require fresh review, Test and approval; even a changed tool description can invalidate the saved selection. Do not silently substitute a similarly named tool. After key rotation or system edits, reload the current version and review affected Actions. Use **turn_off_action** to stop new calls; it cannot undo work already sent to your backend.

Before customer use, test a normal request, unavailable record, invalid input, expired key, lost response and customer-authorization rejection. Check the Action from each intended customer channel. Keep read and write credentials narrowly scoped, with a test environment where possible.

[Backend readiness](https://docs.flowlith.com/backend-integration) · [What Iris can do](https://docs.flowlith.com/capabilities) · [Direct APIs and Actions](https://docs.flowlith.com/actions) · [AI workspace](https://docs.flowlith.com/workspace)


---

Source: https://docs.flowlith.com/backend-integration

# Connect a CRM, ERP or HIS

Iris connects to **specific operations you approve**, not an entire database just because you name its software. Your CRM, ERP, HIS or custom backend needs a compatible API, webhook or MCP tool. In this guide, **HIS means hospital information system**.

These are generic integration options, **not a catalogue of prebuilt or certified vendor connectors**. Check your vendor's API availability, account permissions and supported authentication before promising a feature. The examples below describe endpoints your team could provide; they are not Flowlith endpoints or services already installed in your account.

## Choose the connection your backend can support

| What you have | Integration route | What your team must provide |
| --- | --- | --- |
| HTTPS JSON API or automation webhook | API connection and one Action per operation | Final URL, supported authentication, field mappings and useful response fields |
| Compatible token-authenticated remote MCP server | MCP connection and explicitly selected tools | Streamable HTTP endpoint, credential and supported tool definitions; see [MCP setup](https://docs.flowlith.com/mcp) |
| API that needs OAuth token refresh, SOAP/XML, complex schemas or a long job | Your maintained adapter or automation service | Translate a small approved operation into a supported API/MCP contract; manage upstream authorization correctly |
| On-premises service or private network | Your secured integration gateway | A public HTTPS application endpoint that reaches only approved operations; do not expose the database itself |
| Only a website login, spreadsheet export or database credentials | Not a direct Iris connection | A vendor-supported API or an adapter maintained by your team first |

A gateway is not a way around your provider's authorization, permissions or usage restrictions. Iris does not install it, open a VPN, read arbitrary SQL tables or automatically implement vendor protocols. There is no built-in HL7/FHIR connector implied by the HIS examples.

Keep these directions separate:

```text
Owner's AI app → Flowlith MCP → configure and approve an Action
Customer → Iris → approved Action → your API / MCP tool → business system
Website browser → your backend → Flowlith Website API → Iris
```

The [Website API](https://docs.flowlith.com/website) sends messages **to** Iris. It does not expose your CRM to Iris. [Actions](https://docs.flowlith.com/actions) are how Iris calls your system.

## Give your developer this readiness checklist

| Required item | What a useful answer looks like |
| --- | --- |
| One customer journey | “Check an order's delivery status”, not “connect everything” |
| Authorized owner and tenant | Who owns the integration, which company/branch/account it can access |
| Reachable endpoint | Final HTTPS URL on port 443, valid certificate, no redirect or browser-login page |
| Authentication | API: Bearer, named header, Basic or named query key; MCP: Bearer or named header; no auth only for intentionally public data |
| Operation contract | Method, relative path, required inputs, types, units, time zone and examples |
| Customer authorization | How your backend verifies the person and checks record ownership before reading or writing |
| Response contract | Small JSON result, selected display fields, explicit completion state, errors and a reference for reconciliation |
| Write behavior | Exact business effect, later-message confirmation, backend idempotency, conflict rules and status lookup |
| Test environment | Limited test credential, fictional records and a way to inspect or undo test effects |
| Maintenance owner | Who handles credential rotation, schema changes, outages and support |

OpenAPI/Swagger JSON or YAML can help describe operations. It is not required if you supply accurate request/response examples. API documentation does not itself authorize access or activate an Action.

Create a narrowly scoped integration credential. Enter it only through **get_key_link** or the dashboard's dedicated key-entry form. Mark credential headers as secret. Do not put credentials in URLs, plain constants, source code, chat, screenshots or the handoff document. Flowlith's owner-connector sign-in does not sign Iris into your other systems.

The connection fixes the destination host and base path. For `https://api.example.com/iris`, use paths such as `/stock/{sku}`; a customer input must never choose a different server. Your normal API access checks still apply.

## Map a real operation, not a product name

All examples are conditional on your backend implementing and authorizing the operation.

| System | Suitable read operation | Possible confirmed write | Backend responsibilities |
| --- | --- | --- | --- |
| CRM | Public sales availability; customer's own ticket status | Create a lead or support request; update permitted contact preferences | Correct tenant, customer association, allowed fields, duplicate handling |
| ERP | Public stock summary; customer's own order or invoice status | Request an order change or return, if supported | Current stock/prices, ownership, inventory/business validation, no invented fulfillment |
| HIS | Public clinic hours, departments or available appointment slots | Submit a verified administrative appointment request, if approved | Patient/proxy verification, scheduling permissions, minimal data and truthful request-versus-booking status |
| Custom backend | A bounded search or lookup | A narrow create/update/cancel operation | Its existing permissions, validation, audit and transaction rules |

An Action that submits a request is not the same as one that completes it. “Appointment requested” must not become “Appointment booked”; “return requested” must not become “refund paid”. Return a status the customer can understand.

## Authenticate the system and authorize the customer separately

The integration key identifies your business integration. It does **not** prove that a customer may see a particular CRM contact, order, invoice or patient record.

Use platform-supplied channel/customer identity where appropriate, mapped as a **system field**, not a customer-editable input. Your backend must still establish the relationship between that identity and its own account or record. A WhatsApp phone, channel ID, typed name, date of birth or order reference alone is not a general identity-verification or patient-authorization process.

For private operations, require your normal secure verification/linking flow, then check the authenticated tenant, linked customer, record ownership, scope and permitted operation on every request. A secure portal or human-assisted verification may be required. Do not ask for passwords or login codes in the conversation; do not put a session token into an Action's customer-input field.

Configure **Bind result to this customer** when appropriate and return the ownership value at that configured path. This is an additional result check, not a replacement for backend authorization. Never just echo the supplied customer identifier next to an unrelated record. A prompt saying “only return their own records” is not an access-control implementation.

## Read example: a public stock summary

Assume a business allows customers to see this stock summary. Authentication is configured privately and omitted from the example. Your server exposes:

```http
GET /iris/stock/MUG-001 HTTP/1.1
Host: api.example.com
```

Return `200 OK` with `Content-Type: application/json`:

```json
{
  "sku": "MUG-001",
  "available": true,
  "stock_status": "in_stock",
  "checked_at": "2026-11-07T09:00:00Z"
}
```

Configure a required bounded `sku` input into the path and select those four response fields. Do not send supplier costs, warehouse credentials or a full inventory export. An order-status variant needs customer authorization; do not copy this public-data assumption to private records.

## Write example: create a CRM service request

This illustrative endpoint **creates a request for review**. It does not book an appointment, issue an invoice or complete the requested service. The backend must authenticate the integration, authorize the linked customer and validate the category before committing anything.

### Connection configuration

Save the credential separately. The example connection ID/version in the next block must be replaced with the real saved connection values.

```json
{
  "name": "Business service API",
  "kind": "api",
  "base_url": "https://api.example.com/iris",
  "auth": { "kind": "bearer" },
  "headers": []
}
```

### Write-action mapping

This is the Action's configuration, not a request body for your CRM. It maps customer inputs and trusted conversation identity into your API's own JSON shape. Mark it **changes data** and complete owner Preview, Test and activation before customer use. Use the [Actions guide](https://docs.flowlith.com/actions) for the surrounding draft workflow.

```json
{
  "mode": "direct",
  "connection_id": "11111111-1111-4111-8111-111111111111",
  "connection_version": 2,
  "method": "POST",
  "input_schema": {
    "type": "object",
    "additionalProperties": false,
    "properties": {
      "category": { "type": "string", "maxLength": 16, "enum": ["sales", "support"] },
      "summary": { "type": "string", "maxLength": 300 }
    },
    "required": ["category", "summary"]
  },
  "fixed_fields": {},
  "system_fields": { "channel": "channel", "customer": "customer_channel_id" },
  "requires_confirmation": true,
  "bind_customer_path": "customer_channel_id",
  "link_hosts": [],
  "timeout_ms": 10000,
  "idempotent_retry": false,
  "direct": {
    "path": "/service-requests",
    "body_type": "json",
    "sign": false,
    "idempotency_header": "Idempotency-Key",
    "placements": [
      { "source": "input", "field": "category", "in": "body", "target": "/category" },
      { "source": "input", "field": "summary", "in": "body", "target": "/summary" },
      { "source": "system", "field": "channel", "in": "body", "target": "/customer/channel" },
      { "source": "system", "field": "customer", "in": "body", "target": "/customer/id" }
    ]
  }
}
```

For a fictional verified **website** customer, after a later customer message confirms the exact proposed request, the resulting body is:

```json
{
  "category": "support",
  "summary": "Please help arrange a product demonstration.",
  "customer": { "channel": "http", "id": "demo-customer-42" }
}
```

Iris sends `POST https://api.example.com/iris/service-requests`, the privately configured authentication, `Content-Type: application/json` and an opaque operation value in `Idempotency-Key`. There is no extra Iris envelope. Your backend must map the channel identity to an authorized customer; this example does not implement that mapping for you.

Return `201 Created` with `Content-Type: application/json` only after the request is committed:

```json
{
  "request_id": "DEMO-REQ-1042",
  "status": "awaiting_review",
  "customer_channel_id": "demo-customer-42",
  "message": "Your service request was received for review."
}
```

Select `request_id`, `status` and `message` for Iris's answer. Keep the ownership field available for the configured binding check, not as a customer-facing message. The sample identity format is for website chat; validate the correct channel-specific ownership value when adapting it to [WhatsApp](https://docs.flowlith.com/whatsapp) or another channel.

## Keep the backend contract small and explicit

- Action inputs support up to **12 flat fields**: bounded strings (up to 500 characters), bounded numbers/integers, booleans, or arrays of bounded strings (up to 20 items). They are not arbitrary nested JSON Schema. Direct field mappings can construct nested JSON for your API.
- Direct API operations use GET, POST, PUT, PATCH or DELETE, with no body, JSON or form encoding as appropriate. GET has no body. File uploads, raw database queries and arbitrary binary protocols are not covered by this connection.
- Keep a direct request body within **16,000 bytes**. Return within the configured timeout, at most **10 seconds**. Decoded responses are limited to **64 KiB**, and Iris receives a smaller selected result. Return short summaries and bounded search results; paginate or aggregate in your backend.
- For structured success, return UTF-8 `application/json`. Plain text is also supported, but it cannot provide structured customer-binding fields. Do not return HTML login pages, PDFs, vendor-specific JSON content types or an empty 204 response as the only success proof.
- Use explicit HTTP errors for rejected requests and safe, short explanations. Never put tokens, stack traces, full records or connection strings into errors. A successful HTTP status is not proof that the requested business effect completed.
- If work must continue asynchronously, return a job reference and truthful `queued`/`pending` status quickly, then provide a separate read-only status Action. Iris does not automatically keep polling or push a later notification just because your API returned 202.

Your API must enforce idempotency: atomically bind the operation key to the authorized tenant/customer, exact payload and saved result alongside the business change. A repeated key must not create a second record; a changed payload must not silently reuse it. The header alone does not deduplicate anything.

Iris does not automatically repeat a sent direct API/MCP write, even after a timeout or an error. **Unknown is not failed.** Keep a safe reference for reconciliation, inspect the system or use an approved read-only status operation, and do not ask the customer to resend the same write as a new request to force success.

## Additional boundary for a hospital system

Start with public information or a limited administrative workflow. Patient-specific scheduling, proxy requests and record access need the hospital's approved verification, authorization, privacy and operational review before connection. No example here establishes healthcare compliance or suitability for clinical use.

Do not send full patient charts, diagnoses, prescriptions, laboratory reports or unnecessary identifiers when a task needs only an availability result or request status. Do not place patient data into shared business Info, Memory, assets or skills. Keep the system of record responsible for access decisions, audit records, scheduling rules and the real outcome.

This guide does not configure diagnosis, triage, treatment recommendations or autonomous clinical decisions. If the intended task requires clinical judgment or protected-record access that has not been approved, keep it outside the integration and use the hospital's existing staff/portal workflow.

## Acceptance tests before enabling customers

| Test | Required result |
| --- | --- |
| Correct example, then missing/extra/wrong-type input | Valid request works; invalid input cannot cause a write |
| Customer A asks for customer B's record | Backend refuses; changing names/IDs in chat does not grant access |
| Another business, branch or tenant | Credential and backend rules prevent cross-tenant access |
| Unverified customer or proxy | Secure verification required before private data or changes |
| Write without later confirmation; then changed details | No write until the exact current request is confirmed |
| Same operation key arrives twice | One business effect; the saved result is returned |
| Timeout after the backend commits | Reconcile the first operation; never create a replacement blindly |
| Backend rejects, rate-limits or returns a queued job | Iris does not report completed success |
| Key rotation or changed endpoint/tool schema | Review, Test and approve the new version before use |
| Real customer-channel test | The reply and the external result both match, not just a green connection label |

Owner Test is a **real request**, not an automatic dry run. Use fictional records and a test account. Verify the downstream record yourself. Only turn on the exact reviewed version after a successful Test and explicit approval. Test every intended channel because identity and available customer fields differ.

## Copy a non-secret handoff to your backend team

```text
System: [CRM / ERP / HIS / custom backend and version]
Owner and tenant/branch: [who authorizes this connection]
First customer journey: [one read or one confirmed write]
Public API docs or redacted examples: [link / attachment]
Final HTTPS base URL or token MCP endpoint: [URL without credentials]
Authentication type: [type only; enter the key through the secure link]
Required inputs and rules: [fields, bounds, enums, units, timezone]
Customer authorization: [existing verified account-linking/ownership flow]
Response fields and real completion state: [minimal useful result]
Idempotency and status lookup: [how duplicate/unknown writes are handled]
Test account and fictional records: [non-secret references]
Support owner: [role/contact approved for sharing]
Do not include tokens, passwords, customer records or patient details.
```

Next: [MCP connections](https://docs.flowlith.com/mcp) · [Configure Actions](https://docs.flowlith.com/actions) · [What Iris can do](https://docs.flowlith.com/capabilities).


---

Source: https://docs.flowlith.com/website

# Flowlith Website Chat — Integration Guide

## Before you start
Open Dashboard → API keys to create a key for your business. Copy it once and confirm that Iris is launched from Home. Website-only businesses do not need a Meta account.
Keep the key in your backend secret manager as FLOWLITH_API_KEY. Do not paste the real key into an LLM prompt.
API endpoint: https://api.flowlith.com/v1/chat

Need a chat box on your site? Use the [website embed example](https://docs.flowlith.com/website-embed).
There is no hosted Flowlith widget or public script tag: your site supplies the interface and a secure backend.
To let Iris call your business systems instead, see [Actions and external tools](https://docs.flowlith.com/actions).

## Check authentication
GET https://api.flowlith.com/v1/auth/check with Authorization: Bearer YOUR_API_KEY.
A 200 {"ok":true} confirms the key only, not installation or agent readiness. No model runs.

```bash
# Run on your server. Read the key from its secret environment, not a pasted command.
curl --fail-with-body https://api.flowlith.com/v1/auth/check \
  -H "Authorization: Bearer $FLOWLITH_API_KEY"
```

401 means invalid/revoked key; 429 means wait for Retry-After; 503 means temporarily unavailable.
Rotate or revoke in Dashboard → API keys. The old key stops accepting new requests immediately.

## Integration requirements
Implement website chat using the existing project stack. Reuse the site's authentication,
backend and database. Do not call Flowlith directly from browser JavaScript.

Flow: customer browser -> your authenticated backend -> Flowlith -> your backend -> browser.
Use the server-only FLOWLITH_API_KEY environment variable; never expose it through public
build variables, client bundles, browser storage, HTML, logs or error messages.
The key authorizes one business. Never accept an API key or business ID from customer input.

### Customer identity
Derive user_id on the server from the authenticated customer account, consistently across visits.
For anonymous customers, issue an unpredictable server-generated ID in a signed, HttpOnly,
Secure, SameSite cookie. Do not let a visitor choose another customer's ID.
Clearing a guest cookie creates a new identity. Do not match users by display name.
IDs must contain only letters, digits and . _ : @ + -; maximum 128 characters.
The same business key and user_id resume the same chat. Other channels are not automatically linked.
The business key alone does not authenticate an individual customer: your backend must enforce this.

### Message identity and ordering
Create one UUID message_id for each new customer message. Persist the ID and exact request
body before sending, scoped to the authenticated customer. Reuse both on every reconnect,
including after page reloads, timeouts or lost streams. Never generate a new ID to force a retry.
Deduplicate duplicate browser submissions on your backend. Allow only one unresolved message
per customer; queue later messages locally until the previous one finishes.

### Request
```http
POST https://api.flowlith.com/v1/chat
Authorization: Bearer YOUR_BUSINESS_API_KEY
Content-Type: application/json
Accept: text/event-stream

{
  "user_id": "customer-42",
  "message_id": "3b1d7d08-3956-4acd-8698-2f1df5a23af1",
  "text": "Hello"
}
```

Omit page_id and session_id: Flowlith derives the business and session from the key and user_id.
Limit text to 2,000 characters before submission; longer text is currently truncated.
Optional image_urls is an array of up to 3 HTTPS image URLs that Flowlith can fetch.
Optional audio_urls is an array containing 1 HTTPS voice-note URL, up to **2 MiB for website chat**.
Audio formats: OGG/Opus, MP3, M4A/MP4, WAV, WebM or FLAC. Photos: JPEG, PNG, WebP or GIF, up to 4 MiB each.
Keep the combined image payload small; sending three maximum-size images does not guarantee all three can be included.
Use direct download URLs with the correct Content-Type; redirects are rejected. URLs must remain usable during preparation.
Do not include passwords or access tokens in public media examples. Flowlith does not forward your business API key to media hosts.
PDF/document attachments and file uploads are not supported by the Website API.
Send text, images, audio, or a combination. Keep the total JSON body below 100,000 bytes.
Use a fresh message UUID for a genuinely new message; the UUID above is only an example.

### Streaming response
A valid request returns HTTP 200 with Content-Type: text/event-stream and keeps that single
connection open. Parse SSE frames incrementally. The stream sends accepted first, optional
keepalive comments while Iris works, then exactly one terminal reply or error event and closes.
There is no polling endpoint and no 202 polling loop. These are final-answer events, not token deltas.

```text
id: YOUR_MESSAGE_UUID
event: accepted
data: {"ok":true,"status":"processing","message_id":"YOUR_MESSAGE_UUID"}

id: YOUR_MESSAGE_UUID
event: reply
data: {"ok":true,"status":"sent","message_id":"YOUR_MESSAGE_UUID","reply":"Hello! How can I help?"}
```

The examples above show the fields your interface needs; events may also contain user_id and an opaque session_id.
Treat those as response metadata, not authority to select another customer.

Only reply with status sent or deduplicated and a matching message_id is success. Render reply
as text or sanitized Markdown, never unsanitized HTML. An error event contains error, message_id
and retryable. Do not treat an ended stream without a terminal event as success.

### Errors and retry policy
400 or 413: fix validation/body size; do not blindly retry.
401: missing, wrong or revoked key. Check your backend API key; stop automatic retries.
403: wrong business identifier. Omit page_id and check server credentials; stop retries.
After accepted, runtime failures arrive as an SSE error event. If retryable is true, reconnect
with the exact same message ID and payload after capped backoff. Never start a replacement message.
agent_not_ready or brand_not_configured means the business agent needs Flowlith setup.
credits_exhausted: the business is out of credits; stop retries until the owner tops up in Billing.
transcription_not_configured or transcription_rejected: stop retries and contact Flowlith with the message ID.
transcription_unavailable: retry the same message after backoff, as indicated by retryable.
429: throttled. Respect Retry-After (currently 60 seconds).
Network failures or a stream ending without reply/error have an unknown outcome. Reconnect
the same POST with the same message ID and payload using capped exponential backoff plus jitter.
The server keeps each stream open for up to five minutes. Never show fabricated success.
Preserve the pending message ID and exact body across process restarts; never resubmit it as new.
Closing the local stream does not cancel processing.
Contact Flowlith about repeated failures with message_id and timestamp, without the API key.
Do not replay historical messages older than 24 hours through normal integration retries.
Completed-message deduplication is retained for seven days; that is not a promise that an unresolved
request is safe to replay for seven days. Keep your own completed-message records and escalate an
old unresolved request instead of assigning it a new ID.

### API limits
60 requests/minute per ingress IP before key lookup (chat and auth checks combined),
then 60 chat connections/minute per business and 20 per customer, including reconnects.
Apply your own authenticated-customer and guest abuse limits before forwarding requests.
These API limits are not a substitute for your website’s abuse protection.
Respect Retry-After when you receive 429. Limiter outages return 503, not unlimited access.

Response times vary by request. Replies use one SSE stream per message.
Build the chat interface in your website; this API does not include a hosted widget.
History retrieval, session deletion and cross-channel identity linking are not available through this API.
Store the customer-visible transcript in your own application if you need a chat-history UI.
Key rotation invalidates the previous key immediately; update your backend secret. History
remains associated with business and customer identity, not the literal key value.

### Integration checklist
- No key appears in browser source, requests to the browser, or logs.
- Reloading or reconnecting preserves the pending message ID and body.
- The backend consumes SSE incrementally and never polls for completion.
- Error events, 429 and network failures do not submit a new logical message.
- Two customers cannot request each other's identity or receive each other's reply.
- Sequential messages with the same customer ID retain conversational context.
- Output is escaped; loading, error and delayed states are visible and accessible.
- Test API responses and complete an end-to-end conversation before enabling customer traffic.

[Website embed example](https://docs.flowlith.com/website-embed) · [How Iris works](https://docs.flowlith.com/how-iris-works) · [Troubleshooting](https://docs.flowlith.com/troubleshooting)


---

Source: https://docs.flowlith.com/website-embed

# Website embed example

## What you are embedding

Flowlith currently provides a server-to-server chat API, **not a hosted chat widget**. There is no Flowlith script URL or iframe to paste into a page. The example below gives your developer a small text-chat interface and a backend handler to connect to it.

The browser calls only your own website. Your backend authenticates the customer, holds the Flowlith key, remembers the pending message, and reads the reply stream. This example returns the completed reply to the browser as JSON; it is not a token-by-token animation. Your hosting must allow a request lasting up to about five minutes and must not cache this route.

Read the [Website API contract](https://docs.flowlith.com/website) first. For a no-code site without a backend, ask its developer to add a secure server-side integration. Putting a key in a page builder, browser automation, or a public build variable is not an alternative.

## 1. Prepare your backend adapters

Reuse your site's existing authentication and database. The handler below is complete request/SSE logic, **not a standalone application**: wire these adapters to your real project before using it. Do not replace them with an unverified browser ID or an in-memory Map in production.

| Adapter | Required behavior |
| --- | --- |
| `origin` | Your exact public HTTPS origin, such as `https://shop.example.com`. |
| `apiKey` | Read `FLOWLITH_API_KEY` from the server secret manager. Never send it to the browser. |
| `authenticate(request)` | Verify your site's session cookie and return `{ id }`, or `null`. Map the account to a stable API-compatible ID. For guests, issue a random identity in a signed, HttpOnly, Secure, SameSite cookie. |
| `allowRequest(customerId)` | Enforce your application's shared per-customer quota, returning a boolean. Add normal edge/IP abuse protection too. Fail closed if the limiter is unavailable. |
| `store.current(customerId)` | Read only this customer's latest record, or return `null`. |
| `store.prepare(customerId, text)` | In one durable transaction, return the existing pending record unchanged, or create a record with a fresh UUID `messageId`, exact `text`, millisecond `createdAt`, and `state: "pending"`. Permit only one pending record per customer, across tabs and servers. |
| `store.finish(customerId, messageId, result)` | Atomically save the terminal reply/error only if this customer's pending ID matches. Keep the ID, original text and creation time; make repeated completion calls idempotent. Return the saved record. Never overwrite a different/newer message. |

All storage queries must be scoped to the authenticated customer and your business. Use your own normal transcript retention policy; never log the key or raw request headers. Keep pending records through restarts. The example deliberately does not add a second authentication system or a new database.

Preparation and sending are separate: the backend saves the request before any API call. If the browser reloads, it recovers the saved message instead of inventing another ID. If an unresolved request is more than 24 hours old, investigate it; do not silently discard it or send it as a new message.

## 2. Add the backend handler

Save this as a server-only module and route `/api/iris` to the returned handler. It uses the standard Fetch API (Request, Response, streams and fetch). Supply the adapters from step 1 at application startup. Do not bundle this file for the browser.

```javascript
export function createIrisChat({ origin, apiKey, authenticate, allowRequest, store }) {
  const json = (body, status = 200, extra = {}) => new Response(JSON.stringify(body), {
    status, headers: { 'Content-Type': 'application/json', 'Cache-Control': 'no-store', ...extra }
  });
  return async function handle(request) {
    try {
      const url = new URL(request.url);
      if (url.pathname !== '/api/iris') return json({ error: 'not_found' }, 404);
      if (!['GET', 'POST'].includes(request.method)) return json({ error: 'method' }, 405, { Allow: 'GET, POST' });
      // Use the configured public origin, not a client-controlled forwarding header.
      if (request.method === 'POST' && request.headers.get('Origin') !== origin)
        return json({ error: 'origin' }, 403);
      const customer = await authenticate(request);
      if (!customer) return json({ error: 'sign_in_required' }, 401);
      if (!/^[A-Za-z0-9._:@+-]{1,128}$/.test(customer.id)) throw Error('identity_mapping');
      if (!await allowRequest(customer.id))
        return json({ error: 'slow_down' }, 429, { 'Retry-After': '60' });
      if (request.method === 'GET') return json({ record: await store.current(customer.id) });
      if (request.headers.get('Content-Type')?.split(';')[0].trim() !== 'application/json')
        return json({ error: 'json_required' }, 415);
      const reader = request.body?.getReader();
      if (!reader) return json({ error: 'invalid_input' }, 400);
      let body = '', bytes = 0;
      const decoder = new TextDecoder();
      try {
        while (true) {
          const { value, done } = await reader.read();
          if (done) break;
          bytes += value.byteLength;
          if (bytes > 16000) { await reader.cancel(); return json({ error: 'too_large' }, 413); }
          body += decoder.decode(value, { stream: true });
        }
      } finally { reader.releaseLock(); }
      let input;
      try { input = JSON.parse(body + decoder.decode()); }
      catch { return json({ error: 'invalid_json' }, 400); }
      if (!input || typeof input !== 'object' || Array.isArray(input)) return json({ error: 'invalid_input' }, 400);
      if (input.operation === 'prepare') {
        if (Object.keys(input).some(key => !['operation', 'text'].includes(key)) ||
            typeof input.text !== 'string' || !input.text.trim() || input.text.length > 2000)
          return json({ error: 'invalid_message' }, 400);
        // Atomically reuse an existing pending row, or persist a new UUID + exact text.
        return json({ record: await store.prepare(customer.id, input.text.trim()) });
      }
      if (input.operation !== 'send' || Object.keys(input).length !== 1)
        return json({ error: 'invalid_operation' }, 400);
      const pending = await store.current(customer.id);
      if (!pending || pending.state !== 'pending') return json({ record: pending });
      if (Date.now() - pending.createdAt >= 24 * 60 * 60 * 1000)
        return json({ error: 'old_pending_message_contact_support', retryable: false, record: pending }, 409);
      const response = await fetch('https://api.flowlith.com/v1/chat', {
        method: 'POST', redirect: 'error', signal: AbortSignal.timeout(310000),
        headers: { Authorization: 'Bearer ' + apiKey, 'Content-Type': 'application/json', Accept: 'text/event-stream' },
        body: JSON.stringify({ user_id: customer.id, message_id: pending.messageId, text: pending.text })
      });
      if (!response.ok) {
        await response.body?.cancel();
        if ([400, 401, 403, 413].includes(response.status))
          return json({ error: 'owner_setup_required', retryable: false, record: pending }, 502);
        return json({ error: 'iris_unavailable', record: pending }, response.status === 429 ? 429 : 503,
          response.status === 429 ? { 'Retry-After': response.headers.get('Retry-After') || '60' } : {});
      }
      if (!response.headers.get('Content-Type')?.includes('text/event-stream')) {
        await response.body?.cancel(); throw Error('unexpected_response');
      }
      const result = await readIrisReply(response.body, pending.messageId);
      if (result.retryable) return json({ error: 'retry_saved_message', record: pending }, 503);
      const record = await store.finish(customer.id, pending.messageId, result);
      return json({ record });
    } catch {
      // A lost response may already have completed. Keep the saved ID for recovery.
      return json({ error: 'unconfirmed_reload_and_retry_saved_message' }, 503);
    }
  };
}

export async function readIrisReply(body, messageId) {
  if (!body) throw Error('missing_stream');
  const reader = body.getReader(), decoder = new TextDecoder();
  let buffer = '';
  try {
    while (true) {
      const { value, done } = await reader.read();
      buffer += done ? decoder.decode() : decoder.decode(value, { stream: true });
      // shortcut: cap one SSE frame at 256 KiB; raise only after reviewing UI limits.
      if (buffer.length > 262144) throw Error('frame_too_large');
      let boundary;
      while ((boundary = /\r?\n\r?\n/.exec(buffer))) {
        const frame = buffer.slice(0, boundary.index);
        buffer = buffer.slice(boundary.index + boundary[0].length);
        let event = 'message'; const data = [];
        for (const line of frame.split(/\r?\n/)) {
          if (line.startsWith('event:')) event = line.slice(6).trim();
          if (line.startsWith('data:')) data.push(line.slice(5).replace(/^ /, ''));
        }
        if (!['reply', 'error'].includes(event)) continue; // accepted and keepalive
        const result = JSON.parse(data.join('\n'));
        if (result.message_id !== messageId) throw Error('message_mismatch');
        if (event === 'reply' && result.ok === true && ['sent', 'deduplicated'].includes(result.status) &&
            typeof result.reply === 'string' && result.reply.length)
          return { state: 'reply', reply: result.reply };
        if (event === 'error' && result.ok === false && typeof result.retryable === 'boolean')
          return { state: 'error', error: 'Iris could not complete this message.', retryable: result.retryable };
        throw Error('invalid_terminal_event');
      }
      if (done) throw Error('stream_ended_without_result');
    }
  } finally { await reader.cancel().catch(() => {}); reader.releaseLock(); }
}
```

The backend consumes SSE frames incrementally, including frames split between network chunks and CRLF line endings. Only a matching terminal reply is success. A retryable error, timeout, network failure, or missing terminal frame leaves the original message pending. Rejected authentication or invalid requests stop ordinary retries until the owner fixes the setup. There is no API polling endpoint: **Retry saved message** reconnects the same POST with its saved ID and text.

The strict Origin check is for this same-origin browser example. Keep your existing session/CSRF protections. Do not enable wildcard CORS, accept a browser-supplied `user_id`, forward arbitrary client headers, or take the API destination from the browser.

## 3. Add the chat form

Place this HTML where chat should appear. Serve the JavaScript in step 4 as `/iris-chat.js` from your own site. Use your existing design system and content-security policy; the example needs no third-party browser script.

```html
<form id="iris-chat" aria-label="Chat with Iris">
  <label for="iris-message">Your message</label>
  <textarea id="iris-message" name="message" maxlength="2000" rows="3" required disabled></textarea>
  <button id="iris-send" type="submit" disabled>Send message</button>
  <button id="iris-retry" type="button" hidden>Retry saved message</button>
</form>
<p id="iris-status" role="status" aria-live="polite">Loading chat…</p>
<div id="iris-reply" aria-label="Iris reply" style="white-space:pre-wrap"></div>
<script src="/iris-chat.js" defer></script>
```

## 4. Add the browser JavaScript

This contains **no API key**. Responses are inserted with `textContent`, not HTML. The saved record comes from your authenticated backend, so it survives reloads without trusting a customer identity from local storage.

```javascript
const form = document.querySelector('#iris-chat');
const input = document.querySelector('#iris-message');
const send = document.querySelector('#iris-send');
const retry = document.querySelector('#iris-retry');
const status = document.querySelector('#iris-status');
const output = document.querySelector('#iris-reply');
let record = null;
let recoverAt = 0;
let setupRequired = false;

async function call(operation, text) {
  const response = await fetch('/api/iris', operation ? {
    method: 'POST', credentials: 'same-origin',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ operation, ...(text === undefined ? {} : { text }) })
  } : { credentials: 'same-origin', cache: 'no-store' });
  const result = await response.json();
  if ('record' in result) record = result.record;
  if (!response.ok) {
    if (result.retryable === false) {
      setupRequired = true;
      throw Error('Ask the business owner to fix this request or account setup, then reload. Do not send a replacement message.');
    }
    if (response.status === 429) {
      recoverAt = Date.now() + Number(response.headers.get('Retry-After') || 60) * 1000;
      throw Error('Please wait before retrying the saved message.');
    }
    throw Error(response.status === 401 ? 'Sign in to your website account first.' :
      'Not confirmed. Retry the saved message; if it keeps failing, contact the business.');
  }
}
function render() {
  input.disabled = send.disabled = record?.state === 'pending';
  retry.hidden = record?.state !== 'pending' || setupRequired;
  if (record) input.value = record.state === 'pending' ? record.text : '';
  output.textContent = record?.state === 'reply' ? record.reply : '';
  status.textContent = record?.state === 'pending' ? 'Message saved. Retry to recover its reply.' :
    record?.state === 'error' ? record.error : 'Ready. Please do not send passwords or payment details.';
}
async function work(task) {
  input.disabled = send.disabled = retry.disabled = true;
  status.textContent = 'Waiting for Iris…';
  try { await task(); render(); }
  catch (error) {
    // Reload state before another attempt; never create a replacement message here.
    try { await call(); render(); } catch { input.disabled = send.disabled = true; retry.hidden = setupRequired; }
    status.textContent = error.message;
  } finally { retry.disabled = false; }
}
form.addEventListener('submit', event => {
  event.preventDefault();
  const text = input.value.trim();
  if (!text || Date.now() < recoverAt) return;
  work(async () => { await call('prepare', text); await call('send'); });
});
retry.addEventListener('click', () => {
  if (setupRequired) return;
  if (Date.now() < recoverAt) { status.textContent = 'Please wait before retrying.'; return; }
  work(async () => { await call(); if (record?.state === 'pending') await call('send'); });
});
work(() => call());
```

## 5. Test before exposing chat to customers

- Verify two signed-in customers cannot see or resume each other's message. Tampered/expired guest cookies must not choose an identity.
- Start a message, reload the page, and recover the same saved ID. Repeat with two tabs and a dropped network response.
- Split SSE frames across arbitrary bytes, including a multibyte character. Check keepalives, CRLF, a missing terminal event, and a mismatched message ID.
- Test an expired key, rate limiting, exhausted credits, inactive Iris, and a stream timeout. Show a real error, never a fabricated reply.
- Inspect the browser bundle and network responses: the Flowlith key must never appear. Do not log it in server errors either.
- Confirm your host permits the long request. If it does not, use a durable background job and your own authenticated status route; do not substitute a public Flowlith polling endpoint, because none exists.
- Send a real customer-style test message with permission. It can use a reply credit and any enabled Actions may have real effects.

This example is text-only and shows the latest saved message, not a complete transcript. Add transcript rendering using your existing customer-scoped storage if needed. For supported photo/voice URLs and exact limits, use the [Website API reference](https://docs.flowlith.com/website). For external-system calls from Iris, use [Actions](https://docs.flowlith.com/actions).


---

Source: https://docs.flowlith.com/actions

# External APIs and tools

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](https://docs.flowlith.com/website), 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](https://docs.flowlith.com/backend-integration). If you are using MCP, start with [How MCP connects](https://docs.flowlith.com/mcp) to distinguish owner setup from customer tools. For examples of what each connection enables, read [What Iris can do](https://docs.flowlith.com/capabilities).

## 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](https://docs.flowlith.com/troubleshooting). For how Actions fit into a conversation, read [How Iris works](https://docs.flowlith.com/how-iris-works); for connecting a customer channel, start with [WhatsApp](https://docs.flowlith.com/whatsapp) or [Website integration](https://docs.flowlith.com/website-embed).


---

Source: https://docs.flowlith.com/whatsapp

# Connect WhatsApp

## What you are connecting

Iris replies to customers who message your connected WhatsApp business number. You authorize the number through Meta’s own signup window, opened from **Connections** in your Flowlith dashboard. You do not need to build a webhook, obtain a developer token or send Flowlith your Meta password.

Three separate steps matter: **connect your number**, **launch Iris**, and **test a real conversation**. A Connected badge is not proof that a customer has received a reply.

## Before you begin

- Sign in to the Flowlith account that owns the business and select the correct workspace.
- Have access to the Meta business and WhatsApp account you intend to connect. If you cannot select them in Meta, ask the person who manages those assets to complete the connection.
- Have access to the business phone number and the verification method Meta offers. Finish phone ownership and display-name requirements inside Meta.
- If this number already has a two-step verification PIN, have that PIN available. Keep it private.
- Allow the Meta popup and keep the original dashboard tab open until setup finishes.
- Check the number’s current use before changing anything. A number already used in a WhatsApp app or another provider may need a different Meta setup or migration path.

**Do not delete an existing WhatsApp account or disconnect another provider just to experiment.** Confirm the migration or eligible Business app connection path with Meta and your current provider first. This guide does not promise that every existing number can move without interruption.

The WhatsApp number you supply as your **contact method during Flowlith signup** is for contacting you. Entering it does not connect a messaging channel. The channel connection happens separately in the dashboard.

## Connect the number

1. Open **Dashboard → Connections → Connect WhatsApp**.
2. Sign in inside the Meta popup using an account with access to the intended business assets.
3. Follow Meta’s steps to choose or create the business portfolio and WhatsApp Business Account offered to you.
4. Choose the exact business phone number customers should message. Complete any phone verification or business details Meta requests. Selecting only an account without a phone does not finish setup.
5. Review and grant the requested WhatsApp messaging and account-management access. Flowlith needs this to connect the selected number and receive and answer messages.
6. Finish the Meta window and return to Flowlith. Wait while the dashboard verifies the selection. If a choice is shown, select the intended account rather than accepting an unfamiliar one.
7. Complete **Finish WhatsApp setup** if Flowlith asks for it, as described below.
8. Wait for the dashboard to reload. Confirm the correct number is displayed as **Connected**, without an incomplete-setup warning.

Never paste Meta access tokens, authorization codes, verification codes or PINs into Iris chat, an LLM prompt, a support report or these documentation examples.

## The six-digit PIN step

For a new phone that is ready to activate, Flowlith may show **Finish WhatsApp setup**:

- Choose a six-digit **two-step verification PIN** for a new phone, or enter the existing PIN for that phone.
- Enter it only in the protected PIN field and select **Activate phone**.
- This is **not** the temporary SMS or voice-call code used to prove ownership of the number.
- Flowlith does not save this PIN. Keep it in your own password manager for future Meta recovery or registration.
- A number that is already ready may not need this step. The absence of a PIN prompt is not itself an error.

If activation is unconfirmed, keep the same PIN and reconnect to check the number’s status before trying another activation. Do not invent a new PIN to work around an uncertain result. If Meta reports that ownership or the display name is not ready, finish that step in WhatsApp Manager first.

Cancelling the PIN dialog does not complete activation and leaves your saved connection unchanged.

## Numbers already using the WhatsApp Business app

Where available, Meta may offer a Business app connection that lets an eligible number continue using the phone app. Availability depends on the number, Meta’s setup options and the connection flow available to your workspace. Do not assume that an ordinary personal WhatsApp account or a number with another provider is eligible.

If Meta offers this path, complete all of its Business app steps before returning to Flowlith. This path may not need Flowlith’s separate PIN dialog. If the option is missing or setup is incomplete, contact Flowlith instead of deleting the app account.

For a workspace with Business app coexistence enabled, when WhatsApp reports that you replied from the phone app, Iris pauses new automatic replies in **that customer’s WhatsApp chat for 30 minutes**. Other customer chats and channels are unaffected. A reply already in progress may still finish. This is not a promise of instantaneous handover, a complete shared inbox or an import of your old app history.

## Launch Iris separately

Return to **Home**. If your account is under review, wait for approval. Review **About business**, save your business details, then select **Launch Iris** when available. Memory is optional.

Connecting a number does not launch an inactive Iris. If Iris is already launched, a newly connected channel can start receiving customer messages. Make sure the business instructions are ready before adding a production number.

## Verify the whole conversation

Use a separate customer WhatsApp account to message the connected business number. Start with a harmless question that does not ask Iris to change data in another system. Normal reply credits apply.

```text
Customer: What are your opening hours?
[Check that the reply matches the hours saved in About business.]

Customer: And are you open on Saturday?
[Check that the follow-up stays in the same conversation.]
```

Your checks should distinguish these outcomes:

| Check | What it proves |
| --- | --- |
| Correct number shown as Connected | The number’s connection was confirmed during setup. |
| Iris launched on Home | Launch was confirmed; this is not a continuous uptime indicator. |
| Customer’s text receives an actual WhatsApp reply | This customer-to-Iris-to-WhatsApp path worked for that test. |
| Follow-up receives a relevant answer | Conversation continuity worked for that thread. |
| Voice note or photo receives a useful reply | That media path worked; a text test alone does not prove it. |

The dashboard customer preview is useful for reviewing Iris’s answers, but it does **not** send a WhatsApp message or prove delivery to a phone. Test any [Actions](https://docs.flowlith.com/actions) separately with permission: they can contact real external systems.

## Common setup problems

| What you see | What to do |
| --- | --- |
| Meta does not open | Allow popups, wait for Meta to load, and retry from Connections. Check browser blocking if it still fails. |
| Your business or number is missing | Check which Meta account is signed in and whether it has access. Finish selecting a number, not only the business account. |
| Phone ownership or name is not ready | Complete the outstanding step in WhatsApp Manager, then connect again. |
| PIN rejected or activation unconfirmed | Check the existing two-step PIN. Reconnect to check status before attempting activation again. |
| Setup incomplete | Follow the dashboard instruction to disconnect the incomplete saved connection, then reconnect. This is not an instruction to delete the WhatsApp account itself. |
| Number is linked to another Flowlith business | Confirm the intended workspace and contact Flowlith; do not create duplicate workspaces to bypass the conflict. |
| Connected but no reply | Check approval, saved business details, launch and credits. Send a fresh customer text. If using Business app coexistence, account for the temporary pause after your manual reply. |
| Account access saved but message setup unconfirmed | Do not treat it as ready. Reconnect as directed or report the issue. |

## Change or disconnect a number

Use **Connections → WhatsApp → Disconnect**, review the confirmation, then connect the replacement. Flowlith does not silently replace a saved account during another login.

Disconnecting removes the saved connection from Flowlith; it does not delete your WhatsApp account. Other connected channels, business details and conversations remain. A reply already in progress may finish. If you also need to remove permissions in Meta, review them in your Meta account separately.

## Share a safe support report

Use **Home → Report an issue**. Include the failing step, time and timezone, and the visible status with private details removed. The report form accepts text; describe what you see. Do not include PINs, tokens, verification codes or private customer conversations.

```text
Channel: WhatsApp
Step: Returned from Meta; number did not show Connected
Time and timezone: [when it happened]
Expected: [what you expected]
Observed: [visible message, with private details removed]
Checks already tried: [popup allowed / correct workspace / status rechecked]
```

Continue with [channels and media](https://docs.flowlith.com/channels), [how Iris works](https://docs.flowlith.com/how-iris-works), or [troubleshooting](https://docs.flowlith.com/troubleshooting).


---

Source: https://docs.flowlith.com/channels

# Channels and customer media

## Choose the channels you need

Iris supports customer conversations on WhatsApp, Instagram, Messenger and your website. Each channel is connected separately in the business dashboard. Connecting one does not grant access to the others.

| Channel | What the owner connects | Where to start |
| --- | --- | --- |
| WhatsApp | An authorized WhatsApp business phone number | [WhatsApp onboarding](https://docs.flowlith.com/whatsapp) |
| Instagram | An Instagram professional business or creator account | Connections → Connect Instagram |
| Messenger | A Facebook Page with messaging access | Connections → Connect Messenger |
| Website | Your server, using a business API key | [Website API](https://docs.flowlith.com/website) and [website embed example](https://docs.flowlith.com/website-embed) |

These connections deliver customer messages. Connecting an external CRM, booking API or other tool is a different task: use [Actions and external systems](https://docs.flowlith.com/actions). An email address supplied during signup is a contact detail, not an enabled email-reply channel.

## Connect Instagram

1. Sign in to Flowlith and select the correct business.
2. Open **Connections → Connect Instagram**. Allow the Instagram popup.
3. Sign in to the intended **business or creator account**. A personal account is not the supported connection type.
4. Approve the account and messaging permissions shown by Instagram.
5. Return to Flowlith and wait for verification. Check the displayed username and **Connected** status.
6. Save **About business**, complete any account review, and launch Iris from **Home** if it is not already launched.
7. Send a harmless DM from a separate customer account and confirm an actual reply, then ask a follow-up.

This uses **Instagram Login**. You do not need to connect a Facebook Page just to connect Instagram. Connecting Instagram does not also connect Messenger.

If access is revoked or expires, reconnect through Connections. A successful connection today is not a guarantee that Instagram access will remain valid indefinitely. Never send your Instagram password or access token through chat or support.

## Connect Messenger

1. Open **Connections → Connect Messenger** in the intended business workspace.
2. Complete Facebook sign-in with an account authorized to manage the intended Page and its messaging access.
3. Approve the requested Page and messaging permissions. If multiple Pages are available, choose the one customers should message.
4. Select **Connect selected account** if the dashboard shows an account picker.
5. Wait for confirmation and check that the correct Page name is displayed.
6. Launch Iris separately if needed, then send the Page a message from a separate customer account and check the reply.

If the Page is missing, review the signed-in Facebook account and granted Page access. Messenger connects a Page, not your personal Facebook inbox.

## Website conversations

Your website sends messages through your own backend to the Flowlith API. The business API key stays on your server, never in a script tag, browser bundle or public repository. Creating a key is not the same as installing a chat widget.

Use the [website integration guide](https://docs.flowlith.com/website) for authentication, customer identity, streaming replies and reconnects. The [embed example](https://docs.flowlith.com/website-embed) shows the browser-to-your-server boundary. A website-only business does not need a Meta account.

## What is shared, and what stays separate?

Your business instructions and shared knowledge help Iris answer across the connected channels. Customer conversation history is scoped to that customer’s channel conversation. Iris does not automatically decide that an Instagram user, a WhatsApp number and a website visitor are the same person.

Do not put individual customer details in shared **About business** or **Memory** notes. Use an approved external system for customer-specific records, with appropriate access and verification. See [how Iris works](https://docs.flowlith.com/how-iris-works).

## Photos

Customers can send up to **three photos per message**, with each photo no larger than **4 MiB**. Supported image types are JPEG, PNG, WebP and GIF. Send smaller images or send them separately if not all are read.

A useful caption tells Iris what to look for:

```text
This is the bouquet style I like. Can you suggest something similar
for pickup on Friday? Please check availability before confirming.
```

Photo understanding is not proof of payment, identity or stock availability. For an important number, address or reference, ask the customer to type it and verify it in the relevant business system.

## Voice notes

Iris uses the **first voice clip in a message**. Keep clips within the applicable limit:

| Channel | Maximum voice clip size |
| --- | --- |
| WhatsApp | 5 MiB |
| Instagram and Messenger | 2 MiB |
| Website API | 2 MiB |

Supported audio formats are OGG/Opus, MP3, M4A/MP4, WAV, WebM and FLAC. Raw AAC and AMR clips are not supported. Renaming a file does not convert it into another format.

Use short, clear recordings. If the clip is too large, unsupported or cannot be understood, the customer may be asked to type the message or resend it. A voice reply test should use the same channel and kind of clip your customers will actually send. The website API reports its errors through its response stream; follow the [API guide](https://docs.flowlith.com/website) rather than repeatedly generating new message IDs.

These limits describe **incoming** voice notes, not a promise that Iris will send an audio reply.

## Customer documents, where available

Document reading is an optional capability. Confirm it is enabled for your business before relying on it. It applies to customer file attachments on WhatsApp, Instagram and Messenger, **not website API file uploads or owner setup uploads**.

- Send **one file per message**, no larger than **20 MiB**. If several files arrive together, only the first is considered.
- Supported reading formats are PDF, DOCX, XLSX/XLSM, ODT/ODS, CSV and plain text. For a presentation or old Office file, export it to PDF first.
- Spreadsheet macros are not run. Formula cells can be blank; provide their values when they matter.
- Scanned pages and images depend on the available reading capability and scan quality. If they cannot be read, send clearer pages or type the important details.
- Very long files can be read only in part. Pay attention if Iris says pages or content were omitted.
- Document reading does not add an extra Flowlith reply credit charge; the normal reply credit still applies.

```text
Please read the attached product list and tell me whether it mentions
the blue ceramic mug. Do not treat this file as proof of current stock.
```

A customer’s file is reference material, not authorization to issue a refund, confirm payment or change business settings. Keep sensitive documents out of test conversations unless they are needed and you have permission to use them.

## Do not assume every attachment is understood

Video, stickers, location shares and other unsupported attachment types are not equivalent to a readable photo or document. Iris may know that such a message arrived without understanding its contents. Ask for a supported attachment or typed details instead.

## Disconnect, reconnect and test again

To change an account, use **Disconnect** for that channel before connecting another. Review the confirmation: a reply already in progress may finish. Disconnecting one channel leaves the other channels and business details in place; it does not delete the provider account.

After changing a channel, test a fresh message and follow-up from a real customer-side account. Keep **connected**, **launched**, and **delivered** as separate checks. For help, use **Home → Report an issue** and follow the [troubleshooting guide](https://docs.flowlith.com/troubleshooting), without sharing credentials or private customer data.


---

Source: https://docs.flowlith.com/workspace

# Your workspace and AI apps

The dashboard is your management workspace. A connected AI app helps with supported setup and management tasks; it does not replace customer-facing Iris or automatically connect your business channels.

## Find the right dashboard page

Open the [dashboard](https://dashboard.flowlith.com/profile) on a desktop or laptop. Check the selected business before changing anything.

| Page | What you manage |
| --- | --- |
| **Home** | Approval, launch status, activity, credits and next steps |
| **Connections** | Instagram, Messenger and WhatsApp authorization |
| **API keys** | The secret your website backend uses to call Iris |
| **Billing** | Available credit purchases, plan and payment history |
| **Actions** | Customer preview, reference assets, skills and external systems |
| **About business** | Business information, called Info in some AI tools |
| **Memory** | Shared business notes, not individual customer records |
| **Settings** | Account email, sign-out, knowledge export and deletion |

## Teach a procedure and add references

1. In **Actions → Your skills**, select **Teach a skill**. Explain when to use it, what to do, what needs confirmation and when your team should step in.
2. Add relevant supporting material. Skill attachments accept PDF, Word (.docx), Markdown or text: up to five files of 1 MB each. Upload success alone does not prove accurate reading; test a question that requires the material.
3. For reusable references, use **Assets → Upload Markdown**. Assets are UTF-8 Markdown, up to 200 KB per file and 20 assets. Assets and skill attachments share a 3.5 MB budget.
4. Use public HTTPS reference links without passwords, query strings or access tokens. Adding a link does not connect an account or guarantee its contents can be read.
5. Save the draft, review all pending changes, then select **Apply to Iris**. This publishes the whole saved set of assets, skills and associated access keys. Current replies finish first; new replies may pause briefly.
6. Wait for **Applied to Iris**, then test a relevant question. Apply does not send a test automatically.

Replace this fictional outline with your business’s real process. Do not claim a refund or notification was sent unless a working integration confirms it.

```markdown
# Explain our returns process

## When to use this
A customer asks whether an item can be returned.

## Steps
1. Ask which product they mean and when it was received.
2. Read the current returns policy asset.
3. Explain the conditions without inventing an exception.
4. For a case needing review, give our approved support route.

## Boundaries
- Do not promise a refund before the team approves it.
- Do not ask for passwords, login codes or full card details.
- Do not say a request was submitted without a confirmed result.

## Example
Customer: Can I return a gift?
Iris: I can help check the policy. Which item is it, and when was it received?
```

For API or tool access, follow [External APIs and Actions](https://docs.flowlith.com/actions). Keep credentials in the dedicated access-key form or secure link, never in instructions, attachments or chat. “Key saved” is not “connection tested.”

## Preview Iris as a customer

**Customer preview** uses your currently applied knowledge, not unsaved or pending drafts. It is a separate conversation, not an administrative chat or a way to read customer records.

Each answered preview uses one credit. Enabled external tools can change real data. Use fictional details and test connections, and check external results yourself. A preview cannot confirm that your WhatsApp, Instagram, Messenger or website integration delivers messages; test that channel separately.

## Connect your AI workspace

For the full owner-sign-in flow, external tool-server requirements and tested configuration examples, read [How MCP connects](https://docs.flowlith.com/mcp).

Use an AI app that supports remote MCP with browser OAuth. Custom connections may depend on its plan and workspace policy. A public client package or successful custom connection does not imply a marketplace listing.

1. In your app’s custom connector or remote MCP settings, name the connection **Flowlith**.
2. Enter the public server address below and choose browser OAuth.
3. Complete Flowlith sign-in in your browser using your account email, enter the email code there, and review **Allow**. Do not put an Authorization header, password or login code into chat or configuration.
4. Enable the connection in your AI conversation if required, then ask for a read-only check.

```text
https://mcp.flowlith.com/mcp
```

```text
List my Iris businesses. Do not change anything or begin onboarding.
Tell me whether Flowlith is connected and signed in.
If I have more than one business, ask which one I mean.
```

An empty list is valid for a new account. Tool discovery alone is not proof that sign-in or business access succeeded. If the AI app cannot call Flowlith, reconnect and finish browser sign-in before continuing.

Clients that accept remote MCP JSON configuration can use this credential-free example. Other clients use their connection form instead.

```json
{
  "mcpServers": {
    "iris": {
      "url": "https://mcp.flowlith.com/mcp"
    }
  }
}
```

The [public Flowlith client package](https://github.com/jubsnfzm/iris-plugin) has client-specific guidance. Imported copies may require the app’s update action; publishing a package update does not automatically refresh an existing import.

## What to ask your connected assistant

For setup:

```text
Help me set up Iris for my business. First list my businesses and confirm
which one we are working on. Ask one onboarding question at a time.
Show proposed business information before saving it.
Wait for Flowlith approval before proposing launch, and ask me first.
Never request keys or verification codes in chat.
```

For external tools:

```text
Help Iris [describe one customer task] using [system name].
Read the public integration docs or tool list before choosing a setup.
Use a secure key-entry link if credentials are needed.
Show the exact Preview. Explain any real effect and ask before Test.
After a successful Test, ask before enabling that exact Action version.
Do not automatically repeat a sent write with an uncertain outcome.
```

A connected assistant can review status, update business information, prepare skills and assets, apply an approved teaching revision, configure supported Actions, launch an approved business and run an approved customer preview. Settings, shared Memory editing, website API keys and Meta sign-in remain browser steps. Ask for the relevant dashboard link instead of sending private information in chat.

## Account-wide controls

**Settings** covers your account, not just the selected business. Changing your email requires verification of both addresses and signs you out everywhere. **Sign out everywhere** ends account access sessions; it does not stop Iris. Reconnect AI apps when authorization is no longer valid.

**Export knowledge** downloads saved business knowledge as JSON, not a complete backup. It excludes credentials and conversation history. Treat exports as private: they can contain your own confidential business material.

**Delete account** is a separate, irreversible action covering all owned businesses. Review the list and notice carefully. It stops Iris and revokes website API access; it does not delete your external Instagram, Facebook or WhatsApp accounts. Cleanup can take time, and external providers’ retention rules still apply.

[Business information](https://docs.flowlith.com/info) · [Shared Memory](https://docs.flowlith.com/memory) · [Troubleshooting](https://docs.flowlith.com/troubleshooting)


---

Source: https://docs.flowlith.com/info

# About business

## What belongs here?

Open **About business** (called Info in some AI tools) in your workspace to describe your business: what you offer, opening hours, delivery areas, policies, supported languages and preferred tone. This is your business profile, not a customer profile.

You do not need to upload or manage a file. Use the editor and select **Save changes**.

## Format

The starter uses compact profile entries separated by **§ on its own line**, to keep related entries separate. Plain text and Markdown are also valid; there is no required schema or front matter. Keep the whole note within **1,375 characters**, including separators and spaces.

```markdown
Business: Bloom Studio
About: Made-to-order bouquets in Dubai.
Owner's expectations: Help customers choose and order bouquets.
§
Products or services: Custom bouquets.
Service area: Dubai. Confirm the address before quoting a fee.
Opening hours: Monday–Saturday, 9am–6pm, Gulf Standard Time.
Website: [your website]
§
Communication preferences:
Preferred tone: Warm, short replies.
Languages: English or Arabic.
Emoji preference: None.
§
Business policies:
- Confirm availability before promising same-day delivery.
- Send refund requests to our team for review.
```

Replace this example with your actual details. Avoid contradictory instructions. Keep multi-step workflows and reference documents in **Actions**. Store API keys only in the dedicated access-key fields, never in these notes.

## Before you launch

Review and save a nonempty Info note, then return to **Home** and select **Launch Iris**. [Memory](https://docs.flowlith.com/memory) is optional; add shared notes now or later. Saving About business alone never launches an inactive Iris.

## Saving changes

The editor shows your last saved details. Before your first save, it may be filled from setup or onboarding. It is not a live mirror of later changes Iris makes.

If your note uses our older four-section template, the editor may arrange the same facts into shorter entries. Review them and select **Save changes** to apply. Opening About business does not change saved data. Custom notes and already-separated entries are left unchanged; no AI rewrites your text.

Save replaces the complete Info note. Current replies finish first; new replies wait briefly while Iris updates safely. **Saved** means the running agent confirmed the update. If Iris has not launched, the note is saved for its next launch instead.

If another tab changed About business, copy your edits before reloading. A pending or unconfirmed update is not a successful apply. Keep your text and follow the displayed status.

Clearing the editor and saving clears About business only. It does not delete chat history, customer records, skills or [Memory](https://docs.flowlith.com/memory).

## Keep it appropriate

Do not put passwords, access tokens, customer phone numbers, order histories or private customer details here. About business is shared context for all of your customers. It cannot override Flowlith’s privacy rules or grant new tool permissions.

[Open About business](https://dashboard.flowlith.com/profile?section=info) · [Memory guide](https://docs.flowlith.com/memory) · [Website API](https://docs.flowlith.com/website)


---

Source: https://docs.flowlith.com/memory

# Memory

## How Memory differs from Info

**[About business](https://docs.flowlith.com/info)** describes your business and preferences. **Memory** holds short, reusable facts and lessons Iris should remember across conversations. **Actions** holds procedures, supporting material and dedicated access-key fields.

Memory is the shared notes editor in your Iris workspace. Iris can also update these notes as it learns. This is shared business knowledge, not a directory of customers or a chat-history editor. Private customer memory remains separate.

## Format

Use plain text or Markdown. Keep facts concise, specific and useful later. Headings are optional; short bullets usually work best. The editor supports **2,200 characters**, including headings and spaces. No special schema or front matter is needed. Iris may separate entries with a line containing §. Keep those separators between existing entries when editing; a new plain note can be one entry.

```markdown
## Useful facts
- Gift wrapping is available for bouquets; check the current fee.
- The sizing guide is at https://example.com/size-guide.

## Lessons
- Ask for the delivery area before recommending a delivery option.
- Product photos are references, not proof of current stock.

## Checks
- Check the store for current prices and availability.
- Verify promotions before mentioning an expiry date.
```

Use real, verified information in place of these examples. Include a source or date when useful. Remove outdated facts rather than adding conflicting versions. Memory is not a substitute for checking live stock, prices or order status.

## Before you launch

Memory is optional before your first launch. Save your business details in [About business](https://docs.flowlith.com/info), then select **Launch Iris** on Home. You can add shared notes here now or later. You control when Iris starts; no separate launch request is needed.

## Loading and saving

Open **Memory** in the sidebar. The editor loads the current notes from your running Iris, or saved notes when stopped. Loading does not launch Iris. A saved update still waiting for launch is shown as staged, not as active knowledge.

Edit the note and select **Save changes**. This replaces the whole shared note; it does not append. Current replies finish before the update. Other files, skills, credentials and conversation history stay intact.

If Iris learns something new after you opened the editor, the old copy will not overwrite it. The save reports a conflict. Copy your edits, reload the latest memory, merge the changes, and save again. Pending means waiting; **Saved** means Iris confirmed it. For an inactive Iris, changes are staged for the next launch.

Iris can continue learning after your save. Clearing Memory does not erase conversation history or private customer records, and later learning can add new shared facts.

## Do not put these in Memory

Never include passwords, tokens, customer names and contact details, individual orders, transcripts or sensitive personal information. All customers share this business context. Memory does not override privacy rules, authorization or action confirmations.

[Open Memory](https://dashboard.flowlith.com/profile?section=memory) · [About business guide](https://docs.flowlith.com/info) · [Website API](https://docs.flowlith.com/website)


---

Source: https://docs.flowlith.com/troubleshooting

# Troubleshooting

Start with the result you expected, not just the last button you clicked. A draft, accepted connection, launched Iris and delivered reply are separate outcomes.

## Iris is not answering

1. Confirm the selected business.
2. Check **Home** for approval. A business under review cannot launch yet.
3. Review **About business** and **Save changes**. An unsaved onboarding draft is not a completed setup.
4. Check launch status. If launch is starting or unconfirmed, recheck before deciding to retry.
5. Check **Connections** for the channel you are testing, or **API keys** for a website. Another channel’s connection does not cover this one.
6. Check **Billing** for the balance and follow the displayed status.
7. Try one simple question with fictional information. If customer preview works but the real channel does not, investigate that channel or your website backend.

**Launched** is not a continuous online monitor. **Connected** means setup was accepted, not guaranteed future delivery. Avoid repeatedly resending messages while the outcome is uncertain.

## I see “Open on desktop”

The management dashboard currently requires a desktop or laptop-sized browser. Open the same link on a computer. The public docs and secure one-time key-entry page work on phones; this does not mean the whole dashboard is mobile-enabled.

## A Meta channel will not connect

Use the intended account and check your permissions for the selected business asset. Finish the complete provider sign-in flow and return to inspect the result. Closing a popup or an interrupted save is not confirmation.

For WhatsApp, follow the [number onboarding checklist](https://docs.flowlith.com/whatsapp), especially for an existing WhatsApp number. Do not delete an account, reset verification or move a number just to experiment. Follow the supported provider flow and ask for help if it cannot complete.

If the dashboard requires disconnecting before replacing an account, review the selected account carefully: disconnecting affects that channel’s ability to receive new messages. Test again after reconnecting.

## I saved something, but Iris uses the old version

| Change | What to check |
| --- | --- |
| About business or Memory | Wait for confirmed saving; pending is not in use yet. |
| Asset or skill | Review all pending changes and **Apply to Iris**; Save leaves a draft. |
| External Action | Review the exact version, Test, then explicitly turn it on. |
| Connection or credential | Linked Actions may need review, a new Test and activation. |
| AI-app client package | Refresh or update the imported copy in that app if needed. |

For conflicts, copy your edits, reload, and merge with the current version. Do not repeatedly overwrite an old version. If Apply is unconfirmed, inspect its status before retrying. Applying teaching does not send a test message.

## Website chat disconnects or repeats a message

Read the [API reference](https://docs.flowlith.com/website) and [embed example](https://docs.flowlith.com/website-embed). This is a server-to-server stream, not a browser request with an exposed business key.

Keep the same customer identity for a continuing chat. Generate a message ID once for a new message, and preserve the same message ID and body when reconnecting it. Do not create a new ID to force an uncertain retry. Respect retry guidance; do not retry terminal errors automatically.

For CORS errors, check that the browser calls your backend, not Flowlith directly. For a rejected key, check server-side configuration and whether it was rotated or revoked. Never print a key into a console, ticket or screenshot.

## A tool Test failed or the result is unknown

Check the [Actions guide](https://docs.flowlith.com/actions) for the integration mode and request/response shape. Verify credentials in the secure form and permissions at the destination. Do not paste credentials into a prompt to help debug.

A Test can perform a real write. A timeout does not prove nothing happened. Inspect the destination before repeating a booking, order, payment-related change or lead creation. A successful read test does not prove a write is configured; a passing old version does not activate a changed version.

If a tool or connection changed, review and test the current setup using safe data. Never enable an Action whose Preview you do not understand.

## The AI app sees tools but not my business

Tool discovery and account authorization are different. Complete browser sign-in, return to the AI app and ask it to list businesses read-only. Check the email address and selected business. An empty list can be correct for a new account.

After account-wide sign-out, an email change or revoked authorization, reconnect through the browser. If your app or workspace disables custom MCP connections, use the dashboard or ask its administrator; do not paste a token into chat to bypass the restriction.

## A document or voice note was not understood

Check [supported channels and media](https://docs.flowlith.com/channels). A successful upload or visible attachment does not guarantee accurate reading. Try a smaller supported file, a clearer recording or the relevant text. If Iris requests a typed or resent message, follow that instruction rather than repeatedly sending the unreadable file.

Remove credentials and unnecessary personal information from troubleshooting samples. Compare important answers with the original document before relying on them.

## Credits or payment look wrong

Check **Billing** for the balance and payment history. An answered customer preview uses credits too. A payment screen closing is not itself proof that credits updated; reload and check the confirmed status before paying again.

If the provider shows a charge but Flowlith has not updated, report the payment reference through support. Never send card details, a UPI PIN, an OTP or an access token.

## Report a useful, safe issue

Use **Report an issue** on Home or in Settings. Flowlith replies to your signed-in email. Include the expected result, observed result, time and smallest reproduction. Redact credentials, private customer information and confidential URLs.

```text
Subject: [Short description]
Business: [Your business name]
Area: [WhatsApp / website / Actions / account / billing]
Time and timezone: [When it occurred]

Steps:
1. [First action]
2. [Next action]

Expected: [The result you wanted]
Observed: [Visible result or non-secret error wording]
Status checked: [Approval / connection / launch / applied version]
Safe test used: [Fictional data or test system]

I removed keys, login codes, customer details and private links.
```

If a secret was exposed, rotate it at the service that issued it and update the secure connection before continuing. Removing it from a message alone does not revoke it.

[Start here](https://docs.flowlith.com/) · [How Iris works](https://docs.flowlith.com/how-iris-works) · [External APIs and Actions](https://docs.flowlith.com/actions)
