Flowie
AI Agents

Build with AI

Flowie Exchange is built to be driven by AI. Point Claude Desktop, Claude Code, Cursor, n8n, or your own custom agent at the API and it can send, receive, and manage Peppol e-invoices as native tool calls — no glue code, no bespoke wrappers. This page is the hub for every AI surface: the MCP servers, the agent-ready docs, and self-service agent onboarding.

Same auth, same quota, same sandbox
Every AI surface is a thin layer over the REST API you already know. MCP tool calls are forwarded to the underlying FastAPI handler with your Authorization header preserved — so JWT, flw_* keys, tenant scoping, rate limits, and sandbox simulators all work identically.

AI tools

Three ways to put Flowie Exchange in front of an agent. Most integrations start with the MCP server.

MCP server

The Flowie Exchange API ships two Model Context Protocol servers so AI agents — Claude Desktop, Claude Code, Cursor, n8n, custom Python agents — can send, receive, and manage Peppol e-invoices as native tool calls.

Endpoints

ModeToolsProductionSandbox
Curated (recommended) 34 https://back.p2p-flowie.com/exchange/mcp https://back.flowie.ink/exchange/mcp
Full 94 https://back.p2p-flowie.com/exchange/mcp/full https://back.flowie.ink/exchange/mcp/full

The curated server exposes only the six tags an agent actually needs: Documents, Directory, Companies, Lifecycle, Compliance, Partners. Admin, sandbox control plane, AFNOR certification, and debug routes are hidden — fewer tokens spent on tool discovery, far fewer "wrong tool" misfires. Pick full only when the agent genuinely needs platform / white-label / certification surface.

Transport is streamable-HTTP (the modern MCP transport, MCP spec 2025-06-18). The legacy SSE transport is no longer mounted.

Authentication

Every request the agent makes is forwarded to the FastAPI handler with the original Authorization header preserved, so the same scoping rules apply: tenant isolation, per-key quotas, sandbox vs live partitioning.

Authorization: Bearer flw_test_your_key_here

Use a flw_test_… key against the sandbox host while you're developing the agent — every test recipient from the sandbox guide is reachable through MCP exactly as it is through REST. Need a key? Bootstrap one in one click, or — if the agent must request its own key on behalf of a real user — see the OAuth consent flow.

Quickstart — Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

{
  "mcpServers": {
    "flowie-exchange": {
      "url": "https://back.flowie.ink/exchange/mcp",
      "transport": "streamable-http",
      "headers": {
        "Authorization": "Bearer flw_test_your_key_here"
      }
    }
  }
}

Restart Claude Desktop. The hammer icon shows 34 tools loaded. Try: "List my last 5 incoming invoices."

Quickstart — Claude Code

In the project directory, drop a .mcp.json file (Claude Code picks it up automatically per project):

{
  "mcpServers": {
    "flowie-exchange": {
      "url": "https://back.flowie.ink/exchange/mcp",
      "transport": "streamable-http",
      "headers": { "Authorization": "Bearer flw_test_your_key_here" }
    }
  }
}

Or register globally so every project sees it:

claude mcp add flowie-exchange https://back.flowie.ink/exchange/mcp \
  --transport streamable-http \
  --header "Authorization: Bearer flw_test_your_key_here"

Quickstart — Cursor / VS Code

In Cursor: Settings → MCP → Add new server, paste the same JSON shape as Claude Desktop. In VS Code with the Continue extension: same JSON under continue.config.mcpServers. Both speak streamable-HTTP natively.

Quickstart — Python (mcp SDK)

For custom agents, the official mcp Python SDK speaks streamable-HTTP directly:

# pip install mcp
import asyncio, os
from mcp.client.streamable_http import streamablehttp_client
from mcp import ClientSession

URL = "https://back.flowie.ink/exchange/mcp"
KEY = os.environ["FLOWIE_KEY"]

async def main():
    async with streamablehttp_client(
        URL, headers={"Authorization": f"Bearer {KEY}"}
    ) as (read, write, _):
        async with ClientSession(read, write) as session:
            await session.initialize()
            tools = await session.list_tools()
            print(f"{len(tools.tools)} tools available")

            # Call a tool by name with REST-style args
            result = await session.call_tool(
                "list_documents",
                arguments={"direction": "incoming", "status": "unread", "limit": 5},
            )
            print(result.content[0].text)

asyncio.run(main())

Tool catalog (curated)

The curated server exposes one MCP tool per FastAPI operation tagged Documents, Directory, Companies, Lifecycle, Compliance, or Partners. The high-leverage ones for agents:

ToolWhat it does
send_documentSend an e-invoice / credit note / order over Peppol.
list_documentsFilter by direction, status, type, date range.
search_documentsFull-text + structured search across all documents.
get_document_structuredFlat, agent-friendly view — every field as a primitive.
validate_documentPre-flight a payload through BIS / EN-16931 rules.
update_lifecycleApprove, reject, mark as paid, dispute.
search_directoryFind Peppol participants by name, VAT, or country.
verify_recipientCheck a Peppol ID can receive a given document type.
resolve_companyLook up by VAT / SIREN — get Peppol ID + enriched profile.
create_companyRegister a sender, auto-publish to the Peppol SMP.
get_compliance_reportLatest PPF (FR) or SDI (IT) report status for a document.

Run tools/list over MCP to enumerate the full set with input schemas and descriptions. Every tool's input schema mirrors the REST endpoint's request body — see the API Reference for the canonical shape.

Common workflow — "What invoices arrived this week?"

The agent picks the right tools from the prompt; you do nothing.

User: "What invoices arrived this week and which ones are still unpaid?"

Agent → list_documents({direction: "incoming", since: "2026-04-26"})
     → for each: get_document_structured({documentId})
     → for each unpaid: get_compliance_report({documentId})
     → summarises totals by supplier, flags the ones past dueDate

Common workflow — "Send an invoice to ACME"

Three tools, one chain. The agent verifies the recipient before sending.

User: "Bill ACME BVBA €4,500 + VAT for April consulting, due in 30 days."

Agent → search_directory({q: "ACME BVBA"})            # finds peppolId
     → verify_recipient({peppolId, documentType: "INVOICE"})
     → send_document({
         type: "invoice",
         from: "comp_abc123",
         to:   "0208:0123456789",
         document: {
           number:    "INV-2026-0451",
           issueDate: "2026-04-30",
           dueDate:   "2026-05-30",
           currency:  "EUR",
           lines: [{
             description: "Consulting — April 2026",
             quantity: 1, unit: "lot",
             unitPrice: 4500.00, vatRate: 21
           }]
         }
       })

The agent sees the returned documentId + deliveryStatus and reports back. Pass an Idempotency-Key at the REST layer if you want retry safety — MCP forwards it as a tool argument.

Common workflow — "Mark INV-0417 as paid"

User: "INV-2026-0417 was paid yesterday — close the loop."

Agent → search_documents({number: "INV-2026-0417"})    # → documentId
     → update_lifecycle({
         documentId,
         status: "paid",
         note: "Paid 2026-04-29 via SEPA"
       })

The lifecycle change automatically triggers PPF (FR) or SDI (IT) reporting where applicable — the agent doesn't need to know about that. Watch compliance.reported on your webhook stream for confirmation. Belgian invoices skip this step (HERMES was decommissioned 2025-12-31).

Common workflow — "Onboard a new supplier"

User: "Add Globex SRL (VAT IT09876543210) as a partner and check they're on Peppol."

Agent → resolve_company({vatNumber: "IT09876543210"})   # enriched profile
     → verify_recipient({peppolId})                     # canReceive: true?
     → save_partner({...})                              # in your CRM/ERP

Direct HTTP (no SDK)

MCP is just JSON-RPC over an HTTP POST. If you don't want the SDK:

# 1. Initialize the session
curl -X POST https://back.flowie.ink/exchange/mcp \
  -H "Authorization: Bearer $FLOWIE_KEY" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json, text/event-stream" \
  -d '{
    "jsonrpc": "2.0", "id": 1, "method": "initialize",
    "params": {
      "protocolVersion": "2025-06-18",
      "capabilities": {},
      "clientInfo": {"name": "curl", "version": "1.0"}
    }
  }'

# 2. List tools
curl -X POST https://back.flowie.ink/exchange/mcp \
  -H "Authorization: Bearer $FLOWIE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}'

# 3. Call a tool
curl -X POST https://back.flowie.ink/exchange/mcp \
  -H "Authorization: Bearer $FLOWIE_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "jsonrpc":"2.0","id":3,"method":"tools/call",
    "params":{
      "name":"list_documents",
      "arguments":{"direction":"incoming","limit":5}
    }
  }'

Errors

MCP errors mirror the underlying REST errors — same codes, same shape, wrapped in JSON-RPC. A 401 from REST surfaces as MCP error -32001 with the original Flowie error code in data.errorCode. See the error catalog for everything you might see.

The two MCP-specific errors:

Rate limits & quotas

MCP calls inherit your REST quota — there's no separate budget. One MCP tools/call = one REST request. Use the same X-Flowie-RateLimit-Remaining header logic to back off; the header is surfaced on the JSON-RPC response envelope under _meta.

Sandbox

Point the agent at https://back.flowie.ink/exchange/mcp with a flw_test_… key and every sandbox feature works: forced errors via X-Sandbox-Force-Error, simulated recipients (0208:SIM_HAPPY, SIM_DISPUTE, TEST_AP_FAIL), lifecycle simulators, the lot. See the sandbox guide for the full menu.

Tip — keep a sandbox profile in Claude Desktop
Claude Desktop supports multiple mcpServers entries. Register both flowie-exchange-sandbox (test key, sandbox URL) and flowie-exchange-prod (live key, prod URL). Then prompt the agent explicitly: "Use the sandbox server to dry-run this."

When to use full vs curated

You can mount both — agents pick the right one based on the host you point them at. There's no auth difference between the two, so the same key works against both URLs.

Docs for agents

The whole documentation site is published in machine-readable form, following the llms.txt convention. Point an agent (or a RAG pipeline) at these instead of scraping HTML — every page carries a <link rel="alternate" type="text/markdown"> so tools can discover them automatically.

ResourceWhat it is
llms.txtPage index with titles and one-line descriptions — a fast lookup so an agent can decide what to fetch.
llms-full.txtThe entire corpus in one file — every page back-to-back as clean Markdown. Drop it straight into a context window.
llms/reference/index.mdDirectory of per-endpoint Markdown slices — one file per API operation, so an agent can pull just the one endpoint it needs.
openapi.jsonThe full OpenAPI 3.1 spec — the same one that generates the MCP tool schemas.

Everything under docs.get-flowie.com is reachable this way: https://docs.get-flowie.com/llms.txt, https://docs.get-flowie.com/llms-full.txt, and one Markdown file per endpoint under https://docs.get-flowie.com/llms/reference/.

Agent onboarding

An agent doesn't need a human to hand it a key. Two self-service paths let it provision access on its own — see the full agent onboarding guide for both.