MCP server
Connect AI tools to Flowtly through the Model Context Protocol at mcp.flowtly.eu.
Connect
claude mcp add --transport http flowtly https://mcp.flowtly.eu/mcp
On this page
Bank Accounts
Tools
| bankAccounts.get | Get one bank account by id — name, currency, bank, and the format its statements are imported in. |
| bankAccounts.list | List the org's bank accounts. Filter by bank, or set hidden to include archived ones. Use it to resolve the bankAccount id that transactions.list filters on. |
| bankAccounts.create | Create a bank account (type, name, currency, defaultImportFormat required). Write. |
| bankAccounts.update | Update a bank account by id. Write. |
Clients
Tools
| clients.get | Get one client by id — name, country, currency, tax id and status. |
| clients.list | List clients (the org's customers). Filter by status, or by externalPaymentCustomerId to find the client behind a payment-provider id. Use it to resolve the client id that invoices.list, deals.list, projects.list and contracts.list all filter on. |
| clients.create | Create a new client record (name, country, currency, status, tinType required). Write. |
| clients.update | Update a client record by id. Write. |
Config Keys
Tools
| configKeys.catalog | List every organization config key the backend recognises, with its type and allowed values. This is the catalog of what is configurable — read it before configs.get or configs.update rather than guessing a key name. Permission is enforced per key by the backend, so a key appearing here does not guarantee the connected user may write it. |
Configs
Tools
| configs.get | Read one organization config value by id, where the id is a key from configKeys.catalog (e.g. organization-logo-url, organization-icon-url). |
| configs.update | Update an organization config value by id (type + name required; permission is enforced per config key by the backend). Write. |
Contracts
Tools
| contracts.get | Get one contract by id — parties, direction, value, cyclic terms and dates. |
| contracts.list | List contracts. Filter by direction (incoming / outgoing), counterparty, project, cyclic, name or tags. Use it to resolve the contract id that contracts.paymentScheduleLines reads and that deals.win can link a won deal to. |
| contracts.paymentScheduleLines | List a contract's payment schedule — the instalments it is expected to be invoiced or paid in. Pass contractId from contracts.list. This is the plan, not the actuals: compare it against transactions.list to see what has really been paid. |
| contracts.create | Create a contract. Write. |
| contracts.update | Update a contract by id. Write. |
| contracts.delete | Delete a contract by id. Write. |
Cost Groups
Tools
| costGroups.list | List cost groups / cost centres — the buckets that costs, suppliers and incoming invoices are filed under. Use it to resolve the costGroup id that suppliers.create requires and that incoming-invoice suggestions propose. |
| costGroups.create | Create a cost group / cost center (name + type required). Write. |
| costGroups.update | Update a cost group / cost center's name or type by id. Write. |
Counterparties
Tools
| counterparties.get | Get one counterparty by id. |
| counterparties.list | List counterparties — every party the org transacts with. The supplier and client flags say which side(s) a counterparty plays, and one record can be both. This is the party on a bank transaction, so it is what incoming invoices and transactions are matched against. Filter by type, supplier, client, cyclic or budgetNeutral. |
CRM Notes
Tools
| crmNotes.get | Get one CRM note by id. |
| crmNotes.list | List notes written on leads and deals. Filter by lead or deal to read the running commentary on one record. |
| crmNotes.create | Add a note to a lead or a deal (body + exactly one of lead/deal). Author is the connected user. Write. |
| crmNotes.update | Update a CRM note’s body by id. Write. |
| crmNotes.delete | Delete a CRM note by id. Write. |
Deal Lost Reasons
Tools
| dealLostReasons.get | Get one deal lost-reason by id. |
| dealLostReasons.list | List the reasons a deal can be marked lost, in order. deals.lose requires a lostReasonId from here. |
Deals
Tools
| deals.get | Get one deal by id — title, client, stage, amount, owner, contact, expected and actual close dates. |
| deals.list | List deals/opportunities — the sales pipeline. Filter by status (open / won / lost), stage, owner, client, lead, or by expectedCloseDate / closedAt ranges. Amounts are minor units with an explicit currency; do not assume the org's default. |
| deals.create | Create a deal/opportunity (title, client, stage required; amountMinor, currency, expectedCloseDate, owner, contact optional). Write. |
| deals.update | Update a deal by id (title, stage, amount, close date, owner, contact). Moving the stage is logged automatically. Write. |
| deals.delete | Delete a deal by id (soft delete). Write. |
| deals.win | Mark a deal won — moves it to a won stage and stamps it closed; optional contractId links an existing contract. Write. |
| deals.lose | Mark a deal lost — requires lostReasonId (from dealLostReasons.list); optional lostReasonNote. Write. |
| deals.reopen | Reopen a won/lost deal back to open. Write. |
Deal Stage Histories
Tools
| dealStageHistories.get | Get one deal stage-change record by id. |
| dealStageHistories.list | List a deal's stage transitions, newest first. Filter by deal. Every deals.update that moves the stage is logged here automatically, so this is how you reconstruct how long a deal sat in each stage — the deal itself only carries its current one. |
Incoming Invoices
Tools
| incomingInvoices.get | Get one incoming (supplier) invoice or supporting document by id, with its OCR'd fields and current match state. |
| incomingInvoices.list | List incoming (supplier) invoices and supporting documents — the accountancy inbox. An incoming invoice IS a document attached to a bank transaction, so exists.transaction=false is how you find documents that are not yet matched to a payment. Filter also by status, relatedMonth, counterparty, project, tags, or hasDetectedProblems. Each document is fingerprinted as externalId 'upload_sha256:<sha256 of the bytes>' — hash a file and look for that externalId here BEFORE incomingInvoices.create, or you will file a duplicate. |
| incomingInvoices.matchCandidates | List the bank transactions that could be the payment for this incoming invoice, ranked by the backend's own matcher. Reach for it when a document has no transaction attached and you need to choose one; prefer these candidates over guessing from amounts yourself. |
| incomingInvoices.suggestions | Read Flowtly's own proposals for an incoming invoice — supplier match, cost group, matching bank transaction, duplicate warning. These are exactly the proposals a human sees in the app. Read them first, then apply one by id with incomingInvoices.applySuggestion, or take them all with acceptAllSuggestions. Pass refresh to recompute rather than serve the cached set. |
| incomingInvoices.suggestionsDebug | Explain WHY an incoming invoice's suggestions came out as they did — the matcher's scoring, for diagnosing a missing or wrong suggestion. Diagnostic only; use incomingInvoices.suggestions for normal work. |
| incomingInvoices.create | File an incoming (supplier) invoice or supporting document into accountancy — pass the bytes as base64 with a fileName and receivedAt. Flowtly OCRs it and suggests a supplier and a matching bank transaction. The file is fingerprinted as externalId 'upload_sha256:<sha256 of the bytes>': to avoid a duplicate, hash the bytes and check incomingInvoices.list for that externalId BEFORE uploading. Write. |
| incomingInvoices.applySuggestion | Accept one of Flowtly's own suggestions on an incoming invoice — the same proposals a human sees in the app (supplier match, cost group, matching bank transaction, duplicate warning). Read them first with incomingInvoices.suggestions, then apply one by its id. Prefer this over guessing: Flowtly's matcher, not the agent, decides what is plausible. Write. |
| incomingInvoices.acceptAllSuggestions | Accept every pending suggestion on an incoming invoice in one call — what a human does with the app's "accept all" button. The server applies, rebuilds, and applies again until nothing new appears: the transaction match does NOT exist until the supplier and amount are applied, so a single pass would leave the document unattached. Returns a report (what was applied, what was refused and why, and the transaction it ended up filed against). Pass dryRun to preview without writing. Never accepts supplier_create or a duplicate warning. Write. |
| incomingInvoices.checkEInvoices | Pull any new KSeF e-invoices into the org — what the app's "Sprawdź e-faktury" button does. Call this before concluding that a supplier's invoice is missing: without it you cannot tell "the supplier never sent it" from "our sync has not run yet". Returns once the fetch is queued; re-read incomingInvoices.list afterwards to see what arrived. Write. |
Invoices
Tools
| invoices.get | Get one outgoing (sales) invoice by id — client, rows, totals, sale and issue dates, status. |
| invoices.list | List outgoing (sales) invoices. Filter by client, tags, search, or a saleDate range. Note that saleDate — not issue date and not creation date — is the field invoices.export filters on, so use the same one here when reconciling an export. |
| invoices.export | Start a zip export of ISSUED invoices for a period (from/to, both YYYY-MM-DD, inclusive) filtered on SALE DATE — not issue or creation date. Only ISSUED invoices are included; drafts and unsent invoices are excluded, but corrections ARE included. Optional client restricts to one client (id or IRI from clients.list). Max 200 invoices per export — if the period has more, narrow it (e.g. export one month at a time); a period with 0 issued invoices is rejected too. This call only enqueues the job (rendering a month can take minutes) — it does NOT return a download link. Poll invoices.exportStatus with the returned exportId until it reports "ready". Write. |
| invoices.exportStatus | Poll the status of a zip export started by invoices.export, by exportId. Once status is "ready", the response includes downloadUrl (a short-lived signed link — expires in 1 hour, see expiresAt), filename, and byteSize; the file's bytes are never returned through this tool. If status is "failed", failureReason explains why. |
Lead Contacts
Tools
| leadContacts.get | Get one lead contact by id. |
| leadContacts.list | List the contact people attached to leads. Filter by lead to read one prospect's contacts, or by email to find which lead a message came from. |
| leadContacts.create | Add a contact person to a lead (lead + name required; email, phone, role, isPrimary optional). Write. |
| leadContacts.update | Update a lead contact by id. Write. |
| leadContacts.delete | Delete a lead contact by id. Write. |
Lead List Memberships
Tools
| leadListMemberships.get | Get one lead-to-list membership by id. |
| leadListMemberships.list | List which leads sit on which outbound prospecting lists, with each one's outreach status (contacted / replied / bounced). Filter by list, lead or status — this is how you read the state of a campaign. |
| leadListMemberships.create | Add a lead to an outbound list (list + lead required; status optional). Write. |
| leadListMemberships.update | Update a lead’s membership in a list — e.g. set outreach status (contacted/replied/bounced). Write. |
| leadListMemberships.delete | Remove a lead from an outbound list. Write. |
Lead Lists
Tools
| leadLists.get | Get one outbound prospecting list by id. |
| leadLists.list | List outbound prospecting lists. Use it to resolve the list id that leadListMemberships.create takes. |
| leadLists.create | Create an outbound prospecting list (name required). Write. |
| leadLists.update | Update an outbound list by id. Write. |
| leadLists.delete | Delete an outbound list by id. Write. |
Lead Lost Reasons
Tools
| leadLostReasons.get | Get one lead lost-reason by id. |
| leadLostReasons.list | List the reasons a lead can be marked lost, in order. |
Leads
Tools
| leads.dedupeCheck | Check whether a prospect is already in the CRM, using the same filters as leads.list (companyName, source, owner, …). Call this BEFORE leads.create: a duplicate lead splits the outreach history across two records, and nothing downstream will merge them for you. |
| leads.get | Get one lead by id — company, website, source, status, owner and the client it converted to, if any. |
| leads.list | List leads — prospect targets, before qualification. Filter by status, source, owner, client, companyName, or createdAt/closedAt ranges. A qualified lead becomes a Client plus an open Deal via leads.convert; until then it lives only here, not in clients.list. |
| leads.create | Create a lead (outbound/inbound prospect target; companyName, source, status, owner, linked client optional). Write. |
| leads.update | Update a lead by id (company, website, source, status, owner, linked client). Write. |
| leads.delete | Delete a lead by id (soft delete). Write. |
| leads.convert | Convert a qualified lead into a Client + one contact per lead-contact + an open Deal. Requires an existing client (the lead’s client or a clientId in the body). Write. |
Lead Stages
Tools
| leadStages.get | Get one lead stage by id. |
| leadStages.list | List the stages a lead moves through, in order. Leads have their own stage set — deals use stages.list, which is a different thing. |
Organizations
Tools
| organizations.get | Get an organization by id. WARNING — this does NOT tell you which organization you are connected to. An OAuth connection is pinned to exactly one org (token-bound), but this endpoint returns any org the connected USER is a member of, so a successful read here reads like confirmation you are working in that org when you may not be. To verify the tenant you are actually operating on, read tenant-scoped data instead — people.list or clients.list — and never start a bulk write on the strength of this call alone. |
People
Tools
| people.get | Get one person/employee record by id — names, emails, phone, manager, and whether they are active. |
| people.list | List people/employees. Filter by isActive, reportsTo (a manager's id), projectMembers.project, or search; page with cursor. People and employees share the same id, so this is how you resolve the employee id that work time, responsibilities, project membership and permission tools all expect. |
| people.create | Create a person/employee record (firstname + lastname required; optional companyEmail, contactEmail, contactPhone). Write. |
| people.update | Update a person/employee record by id (name, companyEmail, contactEmail, contactPhone, etc.). Write. |
| people.delete | Delete an employee/person record by id (e.g. to remove a placeholder/dummy employee). Requires ROLE_EMPLOYEES_MANAGER; the backend runs a delete processor that also detaches related records. High-impact, irreversible. Write. |
| people.setPermissionGroups | Set (replace) a person's permission groups by numeric group ids (see permissionGroups.list — e.g. the "Business Owner" group grants ROLE_ADMIN). Grants access; does NOT create a login or email the person. Write. |
Permission Groups
Tools
| permissionGroups.get | Get one permission group by id, including the ROLE_* strings it grants. |
| permissionGroups.list | List the org's permission groups and the roles each one grants — e.g. the "Business Owner" group grants ROLE_ADMIN. Read this before people.setPermissionGroups: the roles in the response are the authority on what a group actually permits, so you never have to guess from its name. |
| permissionGroups.create | Create a permission group (name required; roles = list of ROLE_* strings it grants). Write. |
| permissionGroups.update | Update a permission group's name, description, or granted roles by id. Write. |
Pipelines
Tools
| pipelines.get | Get one sales pipeline by id. |
| pipelines.list | List sales pipelines. A pipeline owns an ordered set of stages — read them with stages.list filtered by pipeline. |
Projects
Tools
| projects.get | Get one project by id — name, type, client, dates, description and price. |
| projects.list | List projects. Filter by type (fixed-price | time-and-material | non-billable | internal), client.name, employee, name, or dateFrom/dateTo ranges. Use it to resolve the project id that tasks, work-time logging, budgets and contracts all take. |
| projects.create | Create a project (name + type required; type = fixed-price|time-and-material|non-billable|internal; optional dateFrom/dateTo, client, publicDescription, notes, priceNet). Write. |
| projects.update | Update a project by id (name, type, dates, description, etc.). Write. |
Responsibilities
Tools
| responsibilities.get | Get one responsibility by id. |
| responsibilities.list | List responsibilities inside a RACI group. Filter by responsibilityGroup. Responsibilities can nest via parent; people are assigned to them through responsibilityEmployees, not directly. |
| responsibilities.create | Create a responsibility inside a group (responsibilityGroup = group id or IRI, + name, required; optional description; optional parent = another responsibility IRI for nesting). Assign people to it via responsibilityEmployees.create. Write. |
| responsibilities.update | Update a responsibility by id (name, description, parent, responsibilityGroup = group id or IRI). Write. |
Responsibility Employees
Tools
| responsibilityEmployees.get | Get one responsibility assignment by id. |
| responsibilityEmployees.list | List who is assigned to which responsibility, and at what percentage. Filter by employee to read one person's entire RACI load across every group. |
| responsibilityEmployees.create | Assign an employee to a responsibility (responsibility = responsibility id or IRI, employee = employee id or IRI, percentage 0-100, all required; optional targets and description). Write. |
| responsibilityEmployees.update | Update a responsibility assignment by id (percentage, targets, description). Write. |
| responsibilityEmployees.delete | Remove an employee's assignment from a responsibility by id. Write. |
Responsibility Groups
Tools
| responsibilityGroups.get | Get one responsibility group by id. |
| responsibilityGroups.list | List responsibility groups / RACI areas — the top-level "Odpowiedzialności" items, each with an accountable person. Individual responsibilities hang underneath them. |
| responsibilityGroups.create | Create a responsibility group / RACI area (name required; optional description and responsibleEmployee = the accountable person, given as a plain employee id like 6 (from people.list) or the /people/6 IRI). This is the top-level 'Odpowiedzialności' item. Add individual responsibilities under it via responsibilities.create. Write. |
| responsibilityGroups.update | Update a responsibility group by id (name, description, responsibleEmployee = employee id or IRI). Write. |
Stages
Tools
| stages.get | Get one deal stage by id. |
| stages.list | List deal stages, in order. Filter by pipeline. deals.create requires a stage id from here, and moving a deal between stages is what dealStageHistories records. |
Suppliers
Tools
| suppliers.list | List suppliers/contractors — served from /contractors, so "supplier" and "contractor" are the same record. Filter by cyclic for recurring suppliers. Use it to resolve the supplier that a cost, a contract or an incoming invoice is filed against. |
| suppliers.create | Create a new supplier/contractor record (name, tinType, costGroup required). Write. |
| suppliers.update | Update a supplier/contractor's details (name, tax id, payment terms, etc.) by id. Write. |
Tag Definitions
Tools
| tagDefinitions.list | List tag definitions — the tags that can be attached to records, each inside a tag group. tags.create takes a tagDefinition id from here plus the record to attach it to. |
| tagDefinitions.create | Create a tag definition (name, level, tagGroup required) within a tag group. Write. |
Tag Groups
Tools
| tagGroups.list | List tag groups — the containers that organize tag definitions. |
| tagGroups.create | Create a tag group (name required) to organize related tag definitions. Write. |
Task Comments
Tools
| taskComments.list | List comments on project tasks, oldest first. Filter by task to read one task's discussion. |
| taskComments.create | Add a comment to a project task (task id + content). Write. |
Task Lists
Tools
| taskLists.list | List task lists — the board columns/sections tasks are filed into. Filter by project. tasks.create takes a list id from here. |
Tasks
Tools
| tasks.get | Get one project task by id — title, project, status, list, assignees, dates and recurrence. |
| tasks.list | List project tasks. Filter by project, list, status, assignees, isTemplate, or startAt/dueAt ranges. Recurring tasks expose recurrenceParent and recurrenceRule, so a generated occurrence can be traced back to the rule that produced it. To decide whether a task is DONE, compare its status against taskStatuses.list (isClosed) rather than matching on the status name. |
| tasks.create | Create a project task (title + project required; optional status, list, assignees, dueAt, priority). Write. |
| tasks.update | Update a project task by id — change status (incl. mark done), assignees, dueAt, title, etc. Write. |
Task Statuses
Tools
| taskStatuses.list | List the project task statuses, in board order. isClosed marks the done states and isDefault the status a new task gets. Read this before interpreting a task's status — the names are org-configurable, so "Done" is not a reliable string to match on. |
Tax Groups
Tools
| taxGroups.list | List tax groups. Use it to resolve the taxGroup id that taxRules.list filters on and that invoice rows carry. |
| taxGroups.create | Create a tax group (name + type required). Write. |
| taxGroups.update | Update a tax group's name or type by id. Write. |
Tax Rules
Tools
| taxRules.list | List tax rules — the rates and the periods they apply to. Filter by taxGroup. |
| taxRules.create | Create a tax rule. Write. |
| taxRules.update | Update a tax rule by id. Write. |
Transactions
Tools
| transactions.list | List bank transactions — the bank feed that incoming invoices are matched against. Filter by bankAccount, counterpartyRole, cost, ignored, hasDetectedProblems, an orderDate/execDate range, or amount.between. Note orderDate and execDate are different: a payment can be ordered in one month and execute in the next. |
| transactions.suggestions | Read Flowtly's proposals for one bank transaction — which counterparty, cost group or document it should be filed against. The mirror image of incomingInvoices.suggestions, from the money side. |
Work Times
Tools
| workTimes.get | Get a single work-time entry by id — date, minutes, project, notes and the employee it belongs to. |
| workTimes.list | List work-time (logged hours) entries. Filter by date range (date.after / date.before, YYYY-MM-DD) and optionally by employee or project; page with cursor. Each row carries employeeId/employeeName and projectId/projectName, so this is how you export all logged hours for a period. IMPORTANT: org-wide results require ROLE_WORKING_HOURS_VIEWER. Without it the backend does NOT error — it silently returns only the connected user's own entries, so an "everyone's hours" export can come back containing one person and look perfectly fine. If every row belongs to one employee and you did not filter by employee, the response carries a scopeWarning saying so — surface it to the user rather than presenting the result as org-wide. |
| workTimes.log | Log a work-time entry for the connected Flowtly user (date, durationMinutes, project, notes). Write. |
Client Contacts
Tools
| clientContacts.create | Create a contact person for a client (client, type, name, email required). Write. |
Counterparty Bank Accounts
Tools
| counterpartyBankAccounts.create | Attach a bank account to a counterparty (counterparty + accountNumber). Write. |
Organization Logo
Tools
| organizationLogo.upload | Upload/replace the organization's logo (base64 image + contentType + filename). Read the current one via configs.get organization-logo-url. Write. |
Organization Icon
Tools
| organizationIcon.upload | Upload/replace the organization's icon/favicon (base64 image + contentType + filename). Read the current one via configs.get organization-icon-url. Write. |