Zyflow docs

MCP reference

This is the reference for the Zyflow MCP server: 32 tools (13 read, 19 write), 5 prompts and 3 resources that AI apps reach over the Model Context Protocol (MCP). It’s generated from the server’s own tools/list, so every title, description, annotation and parameter here is what your MCP app receives.

Last updated Oct 3, 2026

Zyflow is a lightweight small-business CRM your AI app keeps up to date (Claude, Cursor, ChatGPT Business, any MCP app). Every AI change is labeled.

Generated from server version 1.0.0 on [GENERATION DATE]. The Zyflow MCP reference is rebuilt on every server release and reviewed monthly.

New to Zyflow’s server? The MCP server overview explains sign-in and the safety model (Ask. Match. Label. Undo.) in plain terms. This page is the parameter-level detail. To connect an app, see the setup guides.

Zyflow MCP tools at a glance

Zyflow runs one MCP server at one URL for every app. It has 32 tools, 5 prompts and 3 resources, speaks Streamable HTTP, and accepts an OAuth access token or an API key as a Bearer token.

The server in one table
Item Value
Server name zyflow · title “Zyflow CRM” · version 1.0.0 · icon https://zyflowcrm.com/icon-192.png (192x192 PNG)
Server URL https://app.zyflowcrm.com/mcp, the same for everyone
Transport Streamable HTTP, stateless, plain JSON responses, one JSON-RPC message per POST
Spec versions 2026-07-28; 2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05
Authentication Authorization: Bearer with an OAuth access token (zfo_…) or an API key (zf_live_…)
Scopes crm:read (every read tool) and crm:write (adds the 19 write tools)
Tools 32: 13 read and 19 write. 5 carry destructiveHint. openWorldHint is false on all
Idempotency keys On 13 write tools, kept 24 hours
Prompts 5
Resources 3: two fixed URIs and one template
Request size Up to 16 MB per request; files up to 10 MB through save_file
Billing Your AI’s calls are never billed3
This page as Markdown /docs/mcp.md

Limits by plan (record limit, history window, storage) are on the limits page.

How every tool works

These rules apply to all 32 tools: what a result looks like, how undo works, how customers are found, how retries behave, how dates and amounts are read, and how stored text is handled. The tool entries below don’t repeat them.

Results

Every result starts with one plain sentence saying what happened. Most tools follow it with compact JSON: names next to ids, money formatted, dates resolved and empty fields dropped. structuredContent carries the same content, sentence included, for MCP apps that read only that. Every write that changes something returns a change_id, and passing it to undo reverts everything that call did.

There are four shapes:

  • Sentence plus JSON (every write tool, and upload_link). The text item is the sentence, a blank line, then the JSON. structuredContent is the JSON with the sentence in summary.
  • Lists (list_records, get_history, list_files and list_documents). A count line, then one line per row with its id. structuredContent holds the rows as objects, plus next_cursor when there are more.
  • A document as text (get_customer on a single match, fetch, search, get_briefing and describe_workspace). The text is the answer itself, and structuredContent carries it as text beside the key fields. search and fetch put JSON in the text, in the shape OpenAI documents for search and fetch tools.
  • An image (get_file on an image up to 5 MB). The details come as text and the picture as an image item, with no structuredContent, so your app reads content.

create_document, update_document and get_document also add a resource_link item for the PDF.

A dry run, a repeat Zyflow ignored, or a save with nothing new returns no change_id. A value Zyflow can’t use, such as an invalid phone number or an unreadable date, is skipped rather than failing the call: the rest of the write goes through, and the sentence ends with a Note saying what was skipped and why.

Change IDs and undo

Each write tool call is one change batch, and its id starts with bat_. Write results return it as change_id, and get_history and the “What your AI did” part of get_briefing list it too. Every change in History carries the app’s name, such as “Added by Claude via MCP”.

Passing a change_id to undo reverts the whole call: created records are removed, edits go back, and deletes and merges are restored, folders with their contents. Fields edited again since are kept, and the result says so. Undoing the last invoice issued gives its number back. Settings changes, such as pipelines, custom fields and tag renames, are skipped with a reason. An undo is itself a change with its own change_id, so undoing it redoes the change.1

How far back undo reaches depends on the plan’s history window (see limits → /docs/limits). A change older than that fails with history_limit. Each write tool call runs in one database transaction, so it happens in full or not at all.

Finding the customer

Tools take customers the way people say them: a name, “Sam at Halden”, a phone, an email or an id. When one record clearly matches, the tool goes ahead. When more than one could, nothing is written and the candidates come back with what tells them apart, so your AI can ask which one you mean.4

Say a workspace has Sam Ortiz at Halden and Sam Lee at Fieldhouse, and your AI passes “Sam”:

  • save_contact, save_company and save_deal return a normal result with status “ambiguous” and the candidates. Call again with id to update one, or with create_new: true to add someone new.
  • get_customer returns status “ambiguous”, or “not_found” with the closest records, also as a normal result.
  • remember, log_activity, save_task, update_dues, and the file and document tools that take a customer, deal or link return an error result with code ambiguous_reference, and its text lists the candidates. When nothing matches, the code is unknown_customer or unknown_record, with the closest records named.

In each case the next call passes the id (ct_… for Sam Ortiz) and goes through.

Retries, previews and versions

Of the 19 write tools, 13 accept an optional idempotency_key. Zyflow keeps each key for 24 hours. A retry with the same key and arguments returns the first result, marked “(Replayed: this key was already applied.)”. The same key with different arguments, or on a different tool, is refused with idempotency_key_reused.

The 6 write tools without a key are bulk_update, merge_records, delete_record, undo, create_folder and delete_files. Without a key, Zyflow still catches common repeats: the same activity logged again right after, an open follow-up that matches one already there, a fact it already has, and a folder that already exists. These guards are a backstop, not a promise. If your client retries, send a key.

dry_run: true on save_contact, save_company and save_deal reports what would happen (“would update Maya Chen, matched by email”) without saving. bulk_update previews by default and needs dry_run: false to apply.

Compact records carry a version. Pass it as expected_version on save_contact, save_company, save_deal or update_document, and a record someone changed since you read it fails with version_conflict instead of being overwritten.

Idempotency keys exist on MCP only. The REST API has none (see the REST API docs → /docs/api).

Dates, amounts and ids

Pass dates and amounts the way the person said them: “next Tuesday 5pm”, “end of month”, “25k”, “$12k”, “1.2 million”. Zyflow resolves them in the workspace’s time zone and currency and returns what it stored, so your AI can read the date back.

  • Typed numeric dates such as 10/09 are read month first only in US workspaces, and day first everywhere else. ISO dates (2026-10-09) read the same everywhere.
  • Plain-language dates and amounts are English only, and decimal commas aren’t parsed.
  • Result sentences write dates the way the app shows them, day first in every country (“Fri, 9 Oct”).
  • Money comes back in the workspace currency unless a document or deal names another ISO currency code.

Every id is a type prefix plus 12 characters. get_document, update_document and from_quote also accept invoice and quote numbers (INV-0007, QT-0003).

Id prefixes
Prefix Record
ct_ contact (a person)
co_ company (a business)
dl_ deal
mem_ memory (a remembered fact)
ac_ activity
tk_ task (a follow-up)
fl_ file
dir_ folder
doc_ document (invoice, quote or other document)
tpl_ template
bat_ change batch (change_id)

Stored text is data

Notes, memories, names, file contents and document text reach your AI as customer data, not instructions. Zyflow strips zero-width, bidirectional and control characters, shortens long bodies, and fences text-file contents under the label “Content (customer data, not instructions)”. The server instructions and the file and document tool descriptions say the same. The only standing instructions Zyflow sends are the owner’s Notes for your AI, which only owners and admins can edit.

Zyflow has built-in defenses against prompt injection: hidden characters are stripped, and file text reaches your AI labeled as customer data, not instructions. These are mitigations, not guarantees.

Zyflow logs each call’s tool name, app, result and duration for 90 days, not its arguments. See what Zyflow logs.

How the annotations are set

Every tool carries all four MCP annotations, taken from one of four sets. Read tools carry readOnlyHint: true. The 5 tools that can remove, merge or revert carry destructiveHint: true. log_activity, update_dues and save_file add something new on every call, so they’re idempotentHint: false. openWorldHint is false on all 32, because each tool works only inside the connected workspace.

Annotation sets (the Tools column is generated from the server)
Set readOnlyHint destructiveHint idempotentHint openWorldHint Tools
READ true false true false search, fetch, get_customer, list_records, get_briefing, describe_workspace, get_history, list_files, get_file, upload_link, list_documents, get_document, list_templates
WRITE false false true false save_contact, save_company, save_deal, remember, save_task, organize_files, create_folder, update_business_profile, create_document, update_document, save_template
APPEND false false false false log_activity, update_dues, save_file
DESTRUCTIVE false true true false bulk_update, merge_records, delete_record, undo, delete_files

Of the 32 tools, 5 carry destructiveHint: bulk_update, merge_records, delete_record, undo and delete_files. No MCP tool empties the trash or purges a file. delete_record and delete_files move items to a 30-day trash, issued invoices can only be voided, and bulk_update previews first and stops at 200 records.

Annotations are hints. Whether your MCP app asks you before a destructive call is up to the app.

Why the save tools aren’t marked destructive

save_contact, save_company and save_deal update the fields you pass and never delete a record. Emails, phones and tags are added to what’s there, not replaced. A clash between details returns “ambiguous” and writes nothing. Each call is one change, with the old values kept in History, and undo reverts it.1 expected_version stops a save from overwriting an edit made after your AI read the record.

Why undo is destructive

undo can remove records that the call it reverts had created, so it carries destructiveHint like the other tools that remove things.

Why every write result includes change_id

undo needs it. With the change_id in the result, your AI can reverse its own last action in the same conversation without another lookup, and the same id is what History and get_history show beside the change.

One caveat on idempotentHint

create_document is marked idempotentHint: true, but calling it twice without an idempotency_key makes two numbered documents. update_document with payment adds the amount each time it’s called. Send an idempotency_key with both when your client retries.

Find and read

These 7 read tools find customers, list records, brief the day, describe the workspace and show what changed, and none of them changes anything.

Every tool entry has the same parts:

[tool_name] · [title]

Badge: Read or Write, then the four annotation values

Parameters: the count and the required ones

Description and parameter table: generated from the server

Results, Errors and Example: written below

Under each group's last tool: Back to the tool list

search · Search the CRM

Badge: Read · READ · Parameters: 1, required: query

Results: Text whose content is JSON, {“results”: [...]}, with up to 10 hits. Each hit has id, title, url, type, subtitle and snippet. structuredContent holds the same object. Files and documents aren’t searched here; use list_files and list_documents.

Errors: none of its own.

Example: {“query”: “northwind”}

fetch · Read a record

Badge: Read · READ · Parameters: 1, required: id

Results: Text whose content is JSON: id, title, text, url and metadata. For a contact, company or deal, text is the full brief in Markdown. For a memory, activity or task, it’s a short snippet. structuredContent holds the same object.

Errors: bad_id (the value isn’t a Zyflow id) · not_found.

Example: {“id”: “ct_…”} (Maya Chen’s id, from search)

get_customer · Get everything about a customer

Badge: Read · READ · Parameters: 2, required: customer

Results: On one match, the brief as text, ending with an “Open in Zyflow” link. structuredContent: status “found”, id, type, name, url, detail and text. Several matches return status “ambiguous” with the candidates, and none returns “not_found” with the closest records. Both are normal results, not errors.

Errors: none of its own.

Example: {“customer”: “Maya at Northwind”}

list_records · List and filter records

Badge: Read · READ · Parameters: 32, required: type

Results: A count line (“[total] contacts match; showing [n]”), then one line per record with its id. structuredContent: type, total, next_cursor and items. Pass next_cursor back as cursor for the next page. A company filter that matches several businesses returns status “ambiguous” with the candidates.

Errors: unknown_member (owner isn’t a member) · not_found (no company matches) · bad_cursor.

Example (“customers I haven’t spoken to in a month”): {“type”: “contacts”, “lifecycle”: [“customer”], “inactive_days”: 30, “sort”: “least_recent”}

get_briefing · Daily briefing

Badge: Read · READ · Parameters: 1, required: none

Results: The briefing as text, in the order the Today page uses: Do today, Money, What your AI did (with change_ids), Pipeline and Gone quiet. structuredContent: today, timezone, currency, focus, text and the sections as data.

Errors: none of its own.

Example: {“focus”: “today”}

describe_workspace · Describe this workspace

Badge: Read · READ · Parameters: 0

Results: One sentence with the workspace name, today’s date, time zone, currency and date format, then the workspace as JSON. When the owner has written Notes for your AI, the sentence tells your AI to follow them.

Errors: none of its own.

Example: {}

get_history · Show recent changes

Badge: Read · READ · Parameters: 4, required: none

Results: “[n] change batches:”, then one line per batch: the day, who made it (marked “(AI)” for MCP), a summary, whether it was undone or can’t be undone, and its change_id. structuredContent: batches (change_id, at, by, via, summary, undoable, undoneAt) and has_more. With no since, it covers the last 30 days.

Errors: none of its own.

Example: {“agent”: “Claude”, “limit”: 10}

Save customers and deals

These 3 tools add or update people, businesses and deals, and each looks for the record you already have before it creates one.

save_contact · Add or update a person

Badge: Write · WRITE · Parameters: 27, required: none (pass at least a name, phone, email or id)

Results: A sentence saying what happened and how the record was matched (“matched by email”), with any company it linked or created. JSON: action (created, updated, unchanged, or a dry-run result), contact (with its version), company, possibleDuplicates, warnings and change_id. Similar names come back as “Possible duplicates (not merged)”. When the details could fit several people: status “ambiguous”, the candidates, and nothing written.

Errors: nothing_to_save · unknown_member · version_conflict · record_limit · idempotency_key_reused.

Example (a preview first): {“name”: “Maya Chen”, “company”: “Northwind”, “emails”: [“maya@northwind.example”], “lifecycle”: “customer”, “dry_run”: true}

save_company · Add or update a business

Badge: Write · WRITE · Parameters: 27, required: none

Results: The same shape as save_contact, with company in place of contact.

Errors: nothing_to_save · unknown_member · version_conflict · record_limit · idempotency_key_reused.

Example: {“name”: “Kestrel”, “website”: “https://kestrel.example”, “lifecycle”: “prospect”, “city”: “Denver”, “country”: “US”}

save_deal · Add or update a deal

Badge: Write · WRITE · Parameters: 21, required: none

Results: A sentence with the deal, its stage and amount. JSON: action, deal (with its version), the linked company and contact, warnings and change_id. Without an id, an open deal with the same title for the same business is updated instead of duplicated.

Errors: unknown_member · version_conflict · record_limit · idempotency_key_reused.

Example: {“title”: “Brand refresh”, “company”: “Kestrel”, “stage”: “Proposal”, “amount”: “$12k”, “expected_close”: “end of month”}

Memory, activity, follow-ups and money

These 4 tools record what’s true, what happened, what’s next and what’s owed, each on the customer the person named.

remember · Remember facts about a customer

Badge: Write · WRITE · Parameters: 4, required: customer, facts

Results: “Remembered [n] new facts about [customer] ([k] already known).”, then one line per fact with its category and mem_ id. JSON: memories (action, content, category, id) and a change_id when something new was saved. A fact passed with supersedes archives the old one.

Errors: no_facts · ambiguous_reference · unknown_customer · idempotency_key_reused.

Example: {“customer”: “Maya at Northwind”, “facts”: [{“content”: “Prefers email. No calls before 11am.”, “category”: “preference”, “pinned”: true}]}

log_activity · Log a call, meeting, message or note

Badge: Write · APPEND · Parameters: 8, required: customer, summary

Results: “Logged a [kind] with [customer] on [day] ([ac_ id]).”, plus a “Follow-up:” line when follow_up was passed. JSON: activity_id, follow_up and change_id. The call and its follow-up are one change. The same entry sent again right after is reported as already logged, with no change_id.

Errors: ambiguous_reference · unknown_customer · idempotency_key_reused.

Example (with two Sams, this first call returns ambiguous_reference listing Sam Ortiz at Halden and Sam Lee at Fieldhouse, and writes nothing): {“customer”: “Sam”, “kind”: “call”, “summary”: “Wants the revised proposal by Friday.”, “follow_up”: {“title”: “Send revised proposal”, “due”: “friday”}}

Then, once you say Halden, the same call with “customer”: “ct_…” (Sam Ortiz’s id) saves both and reads back the resolved due date.

save_task · Add, reschedule or complete a follow-up

Badge: Write · WRITE · Parameters: 11, required: none (a new task needs a title)

Results: “Added follow-up: [title] ([tk_ id])” with the resolved due date, or “That follow-up already exists”, or “Updated:” for a change by id. JSON: action, task and change_id.

Errors: title_required · not_found · unknown_member · ambiguous_reference · unknown_customer · idempotency_key_reused.

Example: {“title”: “Call Maya about the renewal”, “customer”: “Maya at Northwind”, “due”: “next tuesday 5pm”}

update_dues · Track money a customer owes

Badge: Write · APPEND · Parameters: 10, required: customer

Results: “[customer] now owes [amount], promised by [day] (was [amount]).”, then the collection reminder it created, updated or completed, and a warning when a business passes its credit limit. JSON: customer, outstanding, outstandingDue, currency, reminder, payment_logged and change_id. add and received change the balance on every call, which is why this tool is APPEND.

Errors: nothing_to_save · ambiguous_reference · unknown_customer · idempotency_key_reused.

Example: {“customer”: “Northwind”, “add”: 4800, “due”: “2026-10-15”, “note”: “Workshop invoice”}

Zyflow records payments; it doesn’t collect them. There are no card payment links.

Bulk, merge, delete and undo

This group holds 4 of the 5 destructive tools, and each is one change that undo can reverse.

bulk_update · Change many records at once

Badge: Write · DESTRUCTIVE · Parameters: 11, required: type

Results: By default a dry run: “Dry run: [n] [type] match ([names], …). Nothing changed yet; run again with dry_run: false to apply.” Applied: “Changed [n] of [m] [type]”, with any failures and the change_id. JSON: matched, changed, unchanged, failed (per record; one failure doesn’t stop the rest), sample and change_id.

Errors: no_target (no ids or filter) · bad_request (more than 200 records) · unknown_member.

Example (“tag everyone in Austin for the meetup”, previewed first): {“type”: “contacts”, “filter”: {“city”: “Austin”}, “add_tags”: [“austin-meetup”]}

merge_records · Merge duplicate records

Badge: Write · DESTRUCTIVE · Parameters: 3, required: type, keep_id, merge_ids

Results: “Merged [n] records into [id]”, with what moved, and the change_id. JSON: kept, merged, moved and change_id. Up to 10 duplicates per call. Files and documents linked to a duplicate don’t move, and deals can’t be merged.

Errors: bad_request (no duplicate, or more than 10) · not_found.

Example: {“type”: “contact”, “keep_id”: “ct_…”, “merge_ids”: [“ct_…”]}

delete_record · Delete a record

Badge: Write · DESTRUCTIVE · Parameters: 1, required: id

Results: “Deleted [name] ([id]). Undo with change_id [bat_…] if this was a mistake.” JSON: deleted and change_id. Contacts, companies, deals, activities, memories and tasks go to the 30-day trash. Draft documents and the workspace’s own templates are deleted, and undo brings them back.

Errors: bad_id · not_found.

Example: {“id”: “tk_…”}

undo · Undo a change

Badge: Write · DESTRUCTIVE · Parameters: 1, required: change_id

Results: “Undid [n] changes, skipped [k].”, then one line per change (“Reverted: …” or “Skipped …” with the reason), then “To redo, undo change_id [bat_…].” JSON: undone (a count), skipped (with reasons) and the undo’s own change_id.

Errors: not_found (no such batch in this workspace) · already_undone · history_limit.

Example: {“change_id”: “bat_…”}

Files

These 7 tools browse, read, store and tidy the business’s files drive. Through Zyflow, your AI can see images up to 5 MB and text files up to 1 MB. It can’t read the text inside PDFs, Word or Excel files.

list_files · List files and folders

Badge: Read · READ · Parameters: 5, required: none

Results: “[n] folders and [m] files [where]:”, one line per folder and file, then “Storage: [used] of [limit] used”, with the trash share and the largest file allowed. structuredContent: summary, folder, folders, files and storage.

Errors: folder_not_found · ambiguous_reference · unknown_record (for linked_to).

Example: {“linked_to”: “Northwind”}

get_file · Read a file

Badge: Read · READ · Parameters: 1, required: file

Results: A details block (name, kind, size, folder, linked records, who added it and an “Open in Zyflow” link), then the image, the fenced text or a download link, by the size and type rules in the description. An image result has no structuredContent. A file in the trash is still found, and marked.

Errors: not_found.

Example: {“file”: “call-notes-maya.md”}

upload_link · Link to upload a file

Badge: Read · READ · Parameters: 2, required: none

Results: “Give the user this link to upload the file into [folder], linked to [customer]: [url]”. The person needs to be signed in. JSON: url, folder and link. A folder or customer it can’t find is dropped with a Note rather than failing. It counts as a read tool because the person does the upload.

Errors: ambiguous_reference.

Example: {“folder”: “Clients/Northwind”, “link_to”: “Northwind”}

save_file · Save a file

Badge: Write · APPEND · Parameters: 10, required: name

Results: “Saved [file]. Open it: [url]”, plus any folders it created. JSON: file and change_id. A name already taken in the folder gets “ (2)”, so every call adds a file, which is why this tool is APPEND.

Errors: file_too_large · storage_full · ambiguous_reference · unknown_record · idempotency_key_reused.

Example: {“name”: “call-notes-maya.md”, “text”: “Call with Maya Chen: wants the revised quote by Friday.”, “folder”: “Clients/Northwind”, “company”: “Northwind”}

organize_files · Move, rename, describe or link files

Badge: Write · WRITE · Parameters: 7, required: files

Results: A sentence, one line per file changed, then “Undo with change_id [bat_…].” JSON: changed and change_id. Up to 50 files per call; rename works on one file at a time.

Errors: rename_one · not_found · ambiguous_reference · idempotency_key_reused.

Example: {“files”: [“fl_…”, “fl_…”], “move_to”: “Clients/Kestrel/2026”, “link_to”: “Kestrel”}

create_folder · Create a folder

Badge: Write · WRITE · Parameters: 1, required: path

Results: “Created folder [path] ([dir_ id])”, or “Folder [path] already exists; nothing changed.”, with an “Open it” link. JSON: folder, created and a change_id only when something was created. Paths nest up to 10 levels.

Errors: path_required.

Example: {“path”: “Clients/Kestrel/2026”}

delete_files · Move files to the trash

Badge: Write · DESTRUCTIVE · Parameters: 1, required: files

Results: “Moved [what] to the trash: [names].”, with the change_id that brings them back. JSON: trashed and change_id. Up to 50 items per call.

Errors: not_found.

Example: {“files”: [“old-logo.png”]}

Business profile

A single tool saves the details of the business itself, which documents print and every AI app receives.

update_business_profile · Update the business profile

Badge: Write · WRITE · Parameters: 18, required: none

Results: “Updated the business profile:”, one line per field changed, then “Undo with change_id [bat_…].” JSON: changed and change_id. emails and phones replace the whole list, and ai_notes replaces the notes, so pass the current notes with the new line.

Errors: insufficient_role (the person who connected is a member, not an owner or admin; the message tells your AI to ask the owner or an admin) · idempotency_key_reused.

Example (adding one line to Notes for your AI): {“ai_notes”: “<the current notes>\nQuote prices before tax.”}

Every member, and every member’s AI, can see and edit every customer. There are no per-person permissions yet.

Invoices, quotes and documents

These 6 tools read and make invoices, quotes and other documents from templates, and every one of them returns documents_off while documents are switched off in Settings.

list_documents · List invoices, quotes and documents

Badge: Read · READ · Parameters: 8, required: none

Results: “[n] invoices for [customer]:”, one line per document (number, customer, total, what’s still due, status, dates and id), then, for invoices, the totals unpaid, overdue and paid in the last 30 days. structuredContent: documents, next_cursor and totals.

Errors: ambiguous_reference · unknown_record · invalid_date · documents_off.

Example: {“kind”: “invoice”, “status”: [“overdue”]}

get_document · Read an invoice, quote or document

Badge: Read · READ · Parameters: 1, required: document

Results: The document as text (who it’s for, dates, lines, totals, what was paid, status, notes and template field values), a PDF link that works without signing in for 7 days, and an “Open in Zyflow” link, plus a resource_link item for the PDF. structuredContent adds the lines and the document’s version. A PDF that’s missing or failed before is made again here.

Errors: not_found · documents_off.

Example: {“document”: “INV-0007”}

list_templates · List document templates

Badge: Read · READ · Parameters: 2, required: none

Results: The 5 built-ins (Clean, Bold, Classic, Letter, Offer letter) and the workspace’s own, each with the fields it asks for. With template: that template’s full Liquid HTML, its fields and the guide to variables and filters. Built-ins can’t be changed; save an adapted copy with save_template.

Errors: not_found · documents_off.

Example: {“kind”: “invoice”}

create_document · Make an invoice, quote or document

Badge: Write · WRITE · Parameters: 22, required: kind

Results: A one-line summary, “PDF (no sign-in needed, valid for 7 days): [link]” and “Open in Zyflow: [url]”, plus a resource_link item for the PDF. JSON: document (id, number, kind, status, customer, currency, total, balance_due, dates, template, url, pdf and version), warnings and change_id. If the PDF can’t be made, the document is still saved and get_document tries the PDF again. Repeat calls without an idempotency_key make a second numbered document.

Zyflow works out totals, tax and numbering. Unit prices come from what you or your AI enter; there’s no price list.

Errors: invalid_kind (from_quote needs kind “invoice”) · invalid_status · ambiguous_reference · unknown_record · documents_off · idempotency_key_reused.

Example (“invoice Northwind for 3 workshop days at $1,200, plus 10% tax”): {“kind”: “invoice”, “customer”: “Northwind”, “items”: [{“name”: “Workshop day”, “quantity”: 3, “unit_price”: 1200, “tax_rate”: 10}], “idempotency_key”: “northwind-workshops-oct”}

update_document · Change an invoice, quote or document

Badge: Write · WRITE · Parameters: 23, required: document

Results: The same shape as create_document, with the PDF made again. A payment is added to what was paid, and the invoice is marked paid once it’s paid in full. status “void” is how an issued invoice or quote is canceled.

Errors: version_conflict · invalid_transition (for example, paying an invoice that’s already paid in full) · invalid_payment · items_required · documents_off · idempotency_key_reused.

Example (“Northwind paid $2,000 against INV-0007”): {“document”: “INV-0007”, “payment”: 2000}

save_template · Create or change a document template

Badge: Write · WRITE · Parameters: 11, required: none (a new template needs a name)

The description is the longest of any tool: its first paragraph shows, and the template guide (variables, filters and rules) sits under “Template guide”.

Results: “Created template “[name]” ([tpl_ id]) for invoices and quotes”, the preview link, and the create_document call that uses it. JSON: template (id, name, kinds, fields, version, default_for), preview, warnings and change_id. make_default from a member saves the template and adds a Note that it isn’t the default.

Errors: template_invalid · template_name_taken · name_required · html_required · documents_off · idempotency_key_reused.

Example (copy the Clean design under a new name and make it the default): {“name”: “Our invoice”, “based_on”: “Clean”, “kinds”: [“invoice”, “quote”], “make_default”: true}

Prompts

There are 5 prompts: daily_briefing, prepare_meeting, log_conversation, draft_follow_up and find_duplicates. They appear only in MCP apps that support prompts, often as slash commands. draft_follow_up only drafts, and log_conversation has your AI show you the list before anything is saved.

Each prompt is a message your app sends to your AI on your behalf. Your AI does the reading, sorting and writing; Zyflow answers the tool calls it makes.

Prompts
Name Title Arguments What it asks your AI to do
daily_briefing Plan my day none Call get_briefing, then give a short plan in the order of its Do today list, in under 12 lines, then the money to chase and the deals that need a push, and offer to draft the messages
prepare_meeting Prepare for a meeting customer (required) Call get_customer with detail “full”, then cover who they are, what’s known about them, open deals, follow-ups, money owed, the last three interactions and three talking points
log_conversation Save a conversation to the CRM conversation (required), customer Sort the pasted text into people and businesses, facts, a summary, follow-ups and money owed, show you that list and ask you to confirm, then save it with the matching tools
draft_follow_up Draft a follow-up message customer (required), channel (whatsapp, email or sms; WhatsApp by default) Call get_customer, then draft a short message that picks up from the last interaction, in the customer’s language and tone if those are on record, without sending anything
find_duplicates Find duplicate customers none Use list_records and search to find the same phone or email, or very similar names at the same company, show the pairs side by side, and merge only the ones you confirm

Zyflow sends nothing to your customers. Your AI drafts; you press send.

Resources

There are 3 resources. zyflow://guide gives the data model and good habits (Markdown). zyflow://workspace gives pipelines, stages, custom fields, tags, members and valid values (JSON). zyflow://record/{id} gives the full brief for one contact, company or deal.

Resources
URI Name and title Type Returns
zyflow://guide guide · How to use Zyflow text/markdown The record types and their id prefixes, the habits the server asks AI apps to follow, and example calls
zyflow://workspace workspace · Workspace schema application/json The same workspace data describe_workspace returns
zyflow://record/{id} record · Customer record text/markdown The full brief for a ct_, co_ or dl_ id; any other id gets a one-line text/plain reply

zyflow://record/{id} is a resource template. It can’t be listed or subscribed to: read one record at a time by its id.

Server instructions

When an AI app connects, Zyflow sends server instructions: a fixed guide to its tools, then an “About this business” paragraph and the owner’s Notes for your AI, labeled as standing instructions. The paragraph and the notes together are capped at about 1,500 characters.

The fixed part

Generated from the server: the instructions text, word for word, with a Copy button.

What Zyflow adds for each workspace

After a blank line, Zyflow appends this, filled in from the workspace and its business profile:

About this business (from its Zyflow profile): <business name> (<industry>). <what the business does, up to 500 characters> It is in <country>: amounts are in <currency>, times in <time zone>, and typed dates are read <day first (DD/MM), or month first (MM/DD) in US workspaces>.

The business owner’s standing instructions for AI apps (follow them unless the user asks otherwise):

  • <Notes for your AI>
  • The second and third lines appear only when the owner has written Notes for your AI. Only owners and admins can edit them.
  • Notes can be up to 2,000 characters. When the paragraph and notes would pass about 1,500, the notes are cut and end with “(the rest: describe_workspace → business.aiNotes)”. describe_workspace always returns the full notes.
  • Zyflow caches the text for up to a minute per workspace and drops it the moment the profile changes. MCP apps read instructions when they connect, so a session that’s already open may keep the notes it started with.
  • If the profile can’t be read, the fixed part goes out on its own.5

Protocol versions and transport

Zyflow’s MCP server uses Streamable HTTP at https://app.zyflowcrm.com/mcp. It’s stateless and answers in plain JSON, one JSON-RPC message per POST; batches are refused. It supports the 2026-07-28 MCP spec and 2025-era clients (2025-11-25, 2025-06-18, 2025-03-26 and 2024-11-05). Requests can be up to 16 MB.

Every request needs Authorization: Bearer with an OAuth access token from Zyflow’s sign-in or a Zyflow API key. Without one, the server answers 401 with a WWW-Authenticate header whose resource_metadata points to https://app.zyflowcrm.com/.well-known/oauth-protected-resource/mcp. That’s how MCP apps start sign-in.

  • 2025-era clients are served through the MCP SDK’s stateless legacy fallback, at the same URL.
  • Authentication happens before the protocol layer, and cookies are never accepted.
  • The server answers cross-origin requests from any origin without credentials, so a browser-based client can call it with a bearer token.
  • Responses are plain JSON, not server-sent events.
  • OAuth 2.1 details (discovery, registration, PKCE, token lifetimes) are on the MCP server overview. API keys and the local bridge are covered in the guide to other MCP apps.

Not supported

The server doesn’t use sampling, elicitation, progress notifications or resource subscriptions, and it doesn’t accept JSON-RPC batches. One connection reaches one workspace, and scopes are all-or-nothing: crm:read or crm:read plus crm:write, with no per-tool or per-record limits.

Errors

Errors come at two levels. Problems with the request are HTTP statuses: 401, 403 insufficient_scope, 429 with Retry-After, and 400 for batches. Problems inside a tool come back as a tools/call result with isError and a code, such as version_conflict, idempotency_key_reused or record_limit.

HTTP level (the tool never runs)

Each of these answers with a JSON-RPC error body whose id is null.

HTTP errors
Status When What comes back
401 No Authorization: Bearer header WWW-Authenticate: Bearer realm=”zyflow”, resource_metadata=”https://app.zyflowcrm.com/.well-known/oauth-protected-resource/mcp”, scope=”crm:read crm:write”. Error -32001, “Authentication required. Connect Zyflow to this app first.”
401 invalid_token The token or key is invalid, expired or revoked The same header plus error=”invalid_token”. Error -32001, “The access token is invalid, expired or revoked. Reconnect Zyflow.”
403 insufficient_scope A read-only connection or key calls a write tool WWW-Authenticate with error=”insufficient_scope” and scope=”crm:read crm:write”, so the app can ask for more. Error -32001, “This connection is read-only. Reconnect with write access to change records.”
429 Too many calls in a short burst A Retry-After header in seconds. Error -32000, “Too many requests. Slow down and retry shortly.”
400 A JSON-RPC batch (an array) Error -32600, “JSON-RPC batches are not supported; send one request per POST.”

A request body over 16 MB is refused before it reaches the MCP server.

A connection or API key with only crm:read still lists all 32 tools. Any call to a write tool is refused with HTTP 403 insufficient_scope before the tool runs.

The 429 is a short burst limit that stops runaway scripts. Your AI’s calls are never billed.3

Tool level (a normal tools/call response)

The JSON-RPC call succeeds and the result has isError: true. Its one text item is a plain sentence your AI can pass on, then a numbered Candidates list when there are some, then “(code: …)” on the last line.

Tool error codes
Code Tools What it means and what to do
version_conflict save_contact, save_company, save_deal, update_document The record changed after your AI read it. Read it again, then retry with the new version
idempotency_key_reused The 13 tools with idempotency_key The key was used in the last 24 hours for different arguments or another tool. Use a new key for a new write
record_limit Saves that create a contact, company or deal The workspace is at its plan’s record limit, and nothing was written. See limits
ambiguous_reference Tools that take a customer, deal or link without saving one Several records could match, and the candidates are listed. Ask the person, then retry with the id
unknown_customer, unknown_record The same tools Nothing matches; the closest records are named
insufficient_scope Every write tool A second scope check inside the tool. Over MCP, the HTTP 403 comes first
insufficient_role update_business_profile Only an owner or admin can change the business profile. The message tells your AI to ask one
already_undone undo That change was undone already. To redo it, undo the undo’s change_id
history_limit undo The change is older than the plan’s history window. See limits
not_found Tools that take an id, a file, a document or a change_id Nothing with that reference in this workspace
documents_off The 6 document tools Documents are switched off. An owner or admin turns them on in Settings > Documents
storage_full save_file The workspace has used its storage, trash included
file_too_large save_file Over 10 MB through an AI connection. Give the person a link from upload_link instead
bad_id fetch, delete_record The value isn’t a Zyflow id
nothing_to_save save_contact, update_dues No field to change was passed
template_invalid save_template The HTML failed the check. Each problem has a line and column, and nothing was saved
(no code) Any tool “Zyflow hit an unexpected error. Try again; if it keeps failing, the data was not changed.”

Arguments that don’t match a tool’s input schema are rejected by the MCP SDK before the tool runs.

Warnings aren’t errors. A value Zyflow skipped is named in a Note at the end of the sentence and in warnings, and the rest of the call goes through.

Other codes for single bad values (invalid_date, unknown_member, rename_one, path_required and the rest) are listed under each tool.

Common questions

Is this the same tool list my MCP app sees?

Yes. The page is generated from the server’s own tools/list on every release, and the server version and date are printed at the top. Titles, descriptions, annotations and input schemas are word for word what your app receives. If your app shows something different, compare the version it reports at connect with the one above, and tell us.

Why don’t save_contact and the other save tools carry destructiveHint?

They update the fields you pass and never delete a record. Emails, phones and tags are added, not replaced, and a clash writes nothing and returns “ambiguous”. Each call is one change with the old values kept in History, and undo reverts it.1

How do I undo something an AI app did through MCP?

Pass the change_id from the write result to undo, or find it with get_history. You can also press Undo next to the change in Zyflow’s History. Undo works within your plan’s history window, and settings changes can’t be undone.1

What happens when my client retries a write?

With the same idempotency_key and arguments within 24 hours, Zyflow returns the first result instead of writing again. Without a key, common repeats such as the same activity or a follow-up already there are caught, but not every one. bulk_update, merge_records, delete_record, undo, create_folder and delete_files take no key.

Why can’t I see Zyflow’s prompts in my app?

Prompts appear only in MCP apps that support them, usually as slash commands. Tools and resources work either way, and each prompt is a short message you can type yourself.

Which tools work on a read-only connection?

All 13 read tools. A connection or key with only crm:read still lists every tool, but a write call gets HTTP 403 insufficient_scope before it runs. The consent screen grants what the app asks for, which is read and write for Claude and ChatGPT2 today. For read-only access, use a read-only API key or an MCP client that asks only for crm:read; see the guide to other MCP apps.

  1. 1 Undo works within your plan’s history window: 30 days on Free, up to 3 years on paid plans. Deleted items can be restored for 30 days on every plan. Settings changes, such as pipelines, custom fields and tag renames, are logged but can’t be undone. ↩
  2. 2 Writing to Zyflow from ChatGPT needs developer mode on a ChatGPT Business, Enterprise or Edu plan. It’s in beta, on the web, and an admin switches it on. Claude works on every plan. Checked October 2026. ↩
  3. 3 Your AI app’s calls to Zyflow, through MCP or the REST API, are never billed on any plan, and there are no AI credits. Short burst limits stop runaway scripts. Your AI app’s own subscription and usage limits still apply. ↩
  4. 4 Your AI asks only if your AI app passes Zyflow’s question on to you. Name matching understands English connecting words, such as “Sam at Halden”. ↩
  5. 5 About 1,500 characters of Notes for your AI reach each app when it connects. Whether a model follows them depends on the model. ↩

Last updated Oct 3, 2026

One link for any MCP app.

Spot an error? Tell us

Get your Zyflow link

Free plan. No card.