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
Agreements_get
Tools
| agreements_get | Get one employment agreement by id — type, variant, the dateFrom/dateTo window, hoursPerWeek, and the derived `calculable`, `active` and `status`. agreements_list supplies the id. Requires ROLE_AGREEMENTS_MANAGER or ROLE_MEETING_MANAGER. Read-only. |
Agreements_list
Tools
| agreements_list | List employment agreements — filter by employee (IRI), isActive, type or variant. THE way to answer "why does people_list say this person is inactive": each row carries `calculable` and `active`, and a person is active exactly when they hold one that is both. Also the place to read the agreement `type` codes this org actually uses before calling agreements_create, since an org can add its own. Requires ROLE_AGREEMENTS_MANAGER or ROLE_MEETING_MANAGER. Read-only. |
Agreement Types_get
Tools
| agreementTypes_get | Get one contract type by id — its name or translationKey, `calculable`, `isActive`, `position` and `builtIn`. The id IS the code, so this reads back a type by the same string an agreement stores in `type`. Use it to confirm a type persisted after agreementTypes_create, and to check `calculable` before putting anyone on it. Requires ROLE_USER. Read-only. |
Agreement Types_list
Tools
| agreementTypes_list | List the contract types THIS org can put on an agreement — the values behind `Ludzie > <person> > Umowy > Edytuj umowę`. Read it before agreements_create or agreements_import, because the list is per-tenant: five built-ins ship ("agreement", "annex", "termination", "list-of-intent", "work-experience") and an org can add its own, so a `type` that is valid in one org 422s in another. THE ID IS THE CODE — the `id` on each row is exactly the string `agreements_create` wants in `type`, not a numeric key to look up. `calculable` is the field that decides whether holding this type makes somebody ACTIVE and counts them into the resourcing bench, holiday accrual and the cost base; a non-calculable type leaves them inactive with no error anywhere, which is intended for a type like "list-of-intent" and a silent bug if you picked it by accident. `builtIn` rows carry a translationKey and a null name; custom rows carry a name rendered verbatim and a null translationKey. Requires ROLE_USER. Read-only. |
Allocations_get
Tools
| allocations_get | One allocation by id — a single person's booking on a project, with its dates and percentage. allocations_list finds the id; this reads the full record. An allocation with no employee is an OPEN role (unfilled demand), not a booking. Needs the resourcing module. Read-only. |
Allocations_list
Tools
| allocations_list | List resourcing allocations — date-ranged assignments of a position on a project to an employee (or to nobody yet, an open role). No filters; page with cursor. Each item carries employeeId/employeeName and projectId/projectName already resolved (null employeeId means an open role); positionId is bare — resolve its name via positions_list. source distinguishes sheet-imported rows from ones created directly in Flowtly. Use this to reconcile a resourcing sheet import: read back what landed and compare against what was submitted. |
Asset Bookings_get
Tools
| assetBookings_get | Get one asset booking by id — the asset, its holder, the dates, and whether it has been cancelled. Read-only. |
Asset Bookings_list
Tools
| assetBookings_list | List asset bookings — who or what currently holds each asset, which is the assignment the Assets screen shows and the only place an asset-to-person link actually lives. Each row carries the asset, the holder (`relationName` employee | project plus `relationId`), start/end dates and, once released, `cancelReason` and `cancelledAt`. Filter by `property` to see one asset's history, or by `employee` to see everything one person holds — that second one is what to run before someone leaves. Note `employee` here is the NUMERIC id, not the /people IRI that assetBookings_create takes. Add `exists.cancelledAt: false` to see only what is still held; without it the list includes released bookings too. Read-only. |
Asset Meter Readings_get
Tools
| assetMeterReadings_get | Get one meter reading by id — its meter, date and value. Read-only. |
Asset Meter Readings_list
Tools
| assetMeterReadings_list | List meter readings — the dated values recorded against an asset meter, the raw data the metered-billing split reads. Each row carries the meter, date and value. Use it to read a meter's history: a value that never moves across periods (a stuck or shared meter) bills zero, and a meter with no recent rows is one nobody is reading. Read-only. |
Asset Meters_get
Tools
| assetMeters_get | Get one asset meter by id — the asset it sits on, utility type, unit and external/QR identifier, with its readings. Read-only. |
Asset Meters_list
Tools
| assetMeters_list | List the org's asset meters — the utility/media counters attached to assets (electricity, water, gas, heat). Each carries the asset it sits on, its utility type and unit, and its readings. Filter by `property` (the asset it belongs to) and `utilityType`. Use it to resolve the meter id that readings take, and to spot meters that read zero, are stuck on one value, or sit on a shared/collective meter. Read-only. |
Assets_get
Tools
| assets_get | Get one asset by id — name, status, category (attributeSet), parent, assetCode, serial number, purchase and warranty dates, location and booking settings. Read-only. |
Assets_list
Tools
| assets_list | List the org's assets — the register of physical things it owns or sells, from laptops and desks to apartments, parking spaces and storage units. Filter by status (in-stock | damaged | sold), attributeSet (the category the Assets list groups by), bookingAllowed, or a partial name or serialNumber; order by name, status, serialNumber, boughtAt or warrantyTo. NOT PAGINATED — the whole set comes back in one response, so a large register is one big payload rather than a first page. Use it to resolve the asset id that asset bookings and asset documents take. Read-only. |
Attribute Entity Values_list
Tools
| attributeEntityValues_list | List attribute VALUES — what a specific asset, project, budget or client actually holds for a bound attribute. Each row carries the attribute, the value, and `relationId` naming the entity it belongs to. Read-only. |
Attributes_get
Tools
| attributes_get | Get one attribute definition by id — name, type, whether it is required or multiple, default value and format pattern. Read-only. |
Attributes_list
Tools
| attributes_list | List attribute DEFINITIONS — the named fields (area, floor, price) that categories bind and assets carry values for. Each has a type: number | string | date | state | period. Read-only. |
Attribute Set Attributes_list
Tools
| attributeSetAttributes_list | List the bindings between categories and attribute definitions — which fields appear on which category. Read-only. |
Attribute Sets_get
Tools
| attributeSets_get | Get one attribute set by id — its name, relationName, icon, and the attributes bound to it. Read-only. |
Attribute Sets_list
Tools
| attributeSets_list | List the org's attribute sets — the CATEGORIES an asset, project, budget or client is filed under. Filter by relationName: "property" for asset categories (what the UI calls Typ zasobu and the Assets list groups by), plus "project", "budget" and "client". Reach for this before creating one: a category duplicated by spelling or casing silently splits the list it groups, and nothing in the UI explains why. Read-only. |
Bank Accounts_get
Tools
| bankAccounts_get | Get one bank account by id — name, currency, bank, and the format its statements are imported in. |
Bank Accounts_list
Tools
| 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. |
Banks_get
Tools
| banks_get | Get one bank by id — the institution, not an account held with it. Use bankAccounts_get for the account. |
Banks_list
Tools
| banks_list | List the banks the org's accounts are held with. Hidden banks are INCLUDED by default — pass hidden=false for the pickers' view, or hidden=true to find the retired ones. Use it to resolve the bank id that bankAccounts_list filters on and that bankAccounts_create needs. |
Budgets_employee Pnl
Tools
| budgets_employeePnl | P&L per employee for a budget — what each person's time earned against what they cost. Needs ROLE_BUDGETS_VIEWER. Read-only. |
Budgets_get
Tools
| budgets_get | Get one budget by id — its period, scope and settings. Needs ROLE_BUDGETS_VIEWER. Read-only. |
Budgets_list
Tools
| budgets_list | List the org's budgets — the periods against which income and cost are planned and compared. Use it to resolve the budget id that every pnl tool takes. Needs ROLE_BUDGETS_VIEWER. Read-only. |
Clients_get
Tools
| clients_get | Get one client by id — name, country, currency, tax id and status. |
Clients_list
Tools
| 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. |
Config Keys_catalog
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_get
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). |
Contracts_get
Tools
| contracts_get | Get one contract by id — parties, direction, value, cyclic terms and dates. |
Contracts_list
Tools
| contracts_list | List contracts. Filter by direction — the stored values are "out" (we sell / issue) and "in" (we buy / receive), plus "unknown" — a real, filterable state rather than an error. A contract created by uploading a document starts as "unknown" and stays there until extraction or a person settles it, so omit the filter to get all three: "in" and "out" queried separately DO NOT add up to the whole set (flowtly-mcp#130). NOT "outgoing"/"incoming": those match nothing and come back as an empty list rather than an error. Also filters on 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_payment Schedule Lines
Tools
| 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. Each line's amount is in MINOR UNITS — grosze, not złote: "530000" is 5 300,00, so divide by 100 before reporting a figure to anyone. |
Cost Groups_list
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. |
Counterparties_get
Tools
| counterparties_get | Get one counterparty by id. |
Counterparties_list
Tools
| 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_get
Tools
| crmNotes_get | Get one CRM note by id. |
CRM Notes_list
Tools
| crmNotes_list | List notes written on leads and deals. Filter by lead or deal to read the running commentary on one record. |
Deal Lost Reasons_get
Tools
| dealLostReasons_get | Get one deal lost-reason by id. |
Deal Lost Reasons_list
Tools
| dealLostReasons_list | List the reasons a deal can be marked lost, in order. deals_lose requires a lostReasonId from here. |
Deals_get
Tools
| deals_get | Get one deal by id — title, client, stage, amount, owner, contact, expected and actual close dates. |
Deals_list
Tools
| 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. |
Deal Stage Histories_get
Tools
| dealStageHistories_get | Get one deal stage-change record by id. |
Deal Stage Histories_list
Tools
| 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. |
Departments_list
Tools
| departments_list | The org's departments, with the numeric id each one is referenced by. READ THIS BEFORE people_create or people_update: both accept a `department` IRI and there is no other way to discover a valid one. The collection is unpaginated and ordered by name, so a single call returns every department the org has. Filter by `name` (partial match) or `code` (exact). Rows carry id, name and code; `manager` is a relation and is not included in list rows — read it with people_list from the other side if you need it. Requires ROLE_EMPLOYEES_VIEWER. Read-only. |
Holiday Days Limits_get
Tools
| holidayDaysLimits_get | One entitlement row by id — the amount, the type, the contract variant and the date it takes effect. holidayDaysLimits_list finds the id. Amounts are in SECONDS (#3763). Read-only. |
Holiday Days Limits_list
Tools
| holidayDaysLimits_list | How much leave each person is ENTITLED to, per type — not how much they have taken, which is holidays_list. Filter by employee. A person can hold several rows for one type over time, because a balance gets topped up or corrected: the row IN FORCE is the one with the latest dateFrom that has already arrived, and rows dated ahead are deliberately ignored until then. Amounts are in SECONDS (#3763) — an 8h leave day is 28800. Requires ROLE_HOLIDAYS_MANAGER. Read-only. |
Holiday Requests_list
Tools
| holidayRequests_list | Leave REQUESTS and where they stand — pending, approved, rejected. Distinct from holidays_list, which is booked leave: a request still awaiting a decision is not yet an absence, so plan against holidays_list and use this one to see what is waiting on someone. Supplies the holidayRequestId that holidays_approve and holidays_bulkApprove take. Read-only. |
Holidays_active
Tools
| holidays_active | Who is off RIGHT NOW — every currently-running leave, org-wide, for everyone. This is the tool for 'who is out today', and the one to cross-check before treating resourcingBench_get's freePercent as availability, because the bench does not subtract leave. Unlike holidays_list it applies no project scoping and needs no permission beyond being signed in, so its answer covers the whole organisation. Returns each absence with its type and dates. Read-only. |
Holidays_get
Tools
| holidays_get | One leave record by id, with its type, dates and duration. Get the id from holidays_list or holidays_active. Read-only. |
Holidays_list
Tools
| holidays_list | Booked leave over a period — the planning view, where holidays_active answers only about today. Filter by employee, by date range, or by project. WHAT YOU SEE DEPENDS ON YOUR PERMISSIONS, and a short list is not proof nobody is off: a holidays manager or accountancy viewer gets the organisation, while a project lead or viewer MUST pass a project filter (or ask about themselves) and is refused outright without one — that refusal is a permission boundary, not an empty calendar. Read-only. |
Holiday Types_list
Tools
| holidayTypes_list | The leave types this org uses, with the id each one is referenced by. Read it before holidayDaysLimits_create/update, which need a holidayType IRI and will otherwise be guessed at. The one that is not a holiday in the ordinary sense is `pick-up-day` — time off owed for overtime already worked (Polish *odbior nadgodzin*), which is a GRANTED balance rather than an annual entitlement. Read-only. |
Incoming Invoices_get
Tools
| incomingInvoices_get | Get one incoming (supplier) invoice or supporting document by id, with its OCR'd fields and current match state. |
Incoming Invoices_list
Tools
| 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. |
Incoming Invoices_match Candidates
Tools
| 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. |
Incoming Invoices_suggestions
Tools
| 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. |
Incoming Invoices_suggestions Debug
Tools
| 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. |
Initial Budget Items_list
Tools
| initialBudgetItems_list | List initial budget line items — the planned amounts, by tag, that contractComparison comes back against. Needs ROLE_BUDGETS_VIEWER. Read-only. |
Initial Budgets_contract Comparison
Tools
| initialBudgets_contractComparison | PLANNED versus CONTRACTED, per tag — the initial budget's planned amounts against the sum of contract values actually signed for that project. This is the question 'have we committed more than we budgeted, and where', and it reads directly off the contracts already in the org, so importing contracts makes it answerable without any further work. Amounts are grosze; a mixed-currency project produces a notice rather than a silently wrong total. Needs ROLE_BUDGETS_VIEWER. Read-only. |
Initial Budgets_get
Tools
| initialBudgets_get | Get one initial budget by id, with its items. Needs ROLE_BUDGETS_VIEWER. Read-only. |
Initial Budgets_list
Tools
| initialBudgets_list | List initial budgets — the ORIGINAL plan for a project or investment, as opposed to the live budget it is measured against. Needs ROLE_BUDGETS_VIEWER. Read-only. |
Invoices_get
Tools
| invoices_get | Get one outgoing (sales) invoice by id — client, rows, totals, sale and issue dates, status. |
Invoices_list
Tools
| 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. |
Lead Activities_get
Tools
| leadActivities_get | Get one lead activity (outreach touch) by id. |
Lead Activities_list
Tools
| leadActivities_list | List a lead's outreach touches — its activity timeline (invite sent, replies, calls, follow-ups). Filter by lead to read one prospect's history. This is the structured counterpart to crmNotes_list: activities are the typed, dated touch-log; notes are freeform commentary. |
Lead Contacts_get
Tools
| leadContacts_get | Get one lead contact by id. |
Lead Contacts_list
Tools
| 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. |
Lead List Memberships_get
Tools
| leadListMemberships_get | Get one lead-to-list membership by id. Its status and lastContactedAt are a caller-written snapshot, not live state — see leadListMemberships_list. |
Lead List Memberships_list
Tools
| leadListMemberships_list | List which leads sit on which outbound prospecting lists. Filter by list, lead or status. CAUTION: status and lastContactedAt are a SNAPSHOT written by whoever last imported or updated the membership. They are not derived, and nothing advances them when an activity is recorded — logging a wave of 529 follow-ups moves neither field — so they can be arbitrarily far behind. To answer “when did we last touch this prospect”, read the activity log instead: leadActivities_list for one lead, leadActivities_byList for a whole campaign. leadListMemberships_syncFromActivities reports the gap and can close it. |
Lead Lists_get
Tools
| leadLists_get | Get one outbound prospecting list by id. |
Lead Lists_list
Tools
| leadLists_list | List outbound prospecting lists. Use it to resolve the list id that leadListMemberships_create takes. |
Lead Lost Reasons_get
Tools
| leadLostReasons_get | Get one lead lost-reason by id. |
Lead Lost Reasons_list
Tools
| leadLostReasons_list | List the reasons a lead can be marked lost, in order. |
Leads_dedupe Check
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
Tools
| leads_get | Get one lead by id — company, website, source, status, owner and the client it converted to, if any. |
Leads_list
Tools
| 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. |
Lead Stages_get
Tools
| leadStages_get | Get one lead stage by id. |
Lead Stages_list
Tools
| 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. |
Locations_get
Tools
| locations_get | Get one location by id — its name and office hours. Read-only. |
Locations_list
Tools
| locations_list | List the org's locations — the physical places assets sit, shown in the UI as Lokalizacja. Needs ROLE_LOCATIONS_MANAGER, which unusually gates the READ as well as the write. Read-only. |
Organization Addresses_get
Tools
| organizationAddresses_get | Get one subscription address record by id — name, street, city, postCode, country, and the tax fields. `street` carries the building number when it was entered by hand, and does not when it came from the NIP/GUS lookup. Read-only. |
Organization Addresses_list
Tools
| organizationAddresses_list | List the organization's subscription address records — the address attached to the Flowtly subscription, and the source the mail-footer {{organizationAddress}} renders from. Normally exactly one row. This is NOT the invoice seller address, which lives in the organization-billing-* config keys (configs_get) and is what invoices and KSeF read; the two are maintained separately and routinely disagree. Read both before concluding which one a customer actually edited. Read-only. |
Organizations_get
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_get
Tools
| people_get | Get one person/employee record by id — names, emails, phone, manager, and whether they are active. |
People_list
Tools
| 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. |
Permission Groups_get
Tools
| permissionGroups_get | Get one permission group by id, including the ROLE_* strings it grants. |
Permission Groups_list
Tools
| 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. |
Pipelines_get
Tools
| pipelines_get | Get one sales pipeline by id. |
Pipelines_list
Tools
| pipelines_list | List sales pipelines. A pipeline owns an ordered set of stages — read them with stages_list filtered by pipeline. |
Positions_list
Tools
| positions_list | List positions — the named roles (e.g. "Backend Engineer") that a project allocation fills. No filters; Position has pagination disabled, so this always returns the org's full role catalog in one call. Each item is {id, name, roles}. Use it to resolve the position name behind an allocations_list row's positionId, and to find the position id a resourcing import must match against. |
Project Members_get
Tools
| projectMembers_get | Get one project membership by id — its employee, project and position. Ids come from projectMembers_list or the projectMembers array on projects_get. |
Project Members_list
Tools
| projectMembers_list | List project memberships — WHO CAN SEE WHICH PROJECT. Filter by project (`/projects/{id}`) to read one project's roster, or by employee to read every project one person can reach; each row carries its own id, the employee, the project and the position (employee|tech-lead|account-manager|viewer). Reach for this first when someone reports a project missing from their Projects list or cannot log time against it: an empty roster, or a roster without them in it, IS the explanation — visibility is membership. It is also the id source for projectMembers_update and projectMembers_delete. Note the same person can appear several times on one project, once per position. |
Projects_cost Allocations
Tools
| projects_costAllocations | How costs were split ONTO this project — which transactions and invoice lines were attributed to it, and in what share. Use it to explain a profitability figure rather than just quote it: this is where an unexpected result is traced back to the document that caused it. Requires ROLE_TRANSACTIONS_MANAGER. Read-only. |
Projects_folder Counts
Tools
| projects_folderCounts | How many projects sit in each project FOLDER, as folderId + total + active. The folderId is a tagDefinition id — resolve names with tagDefinitions_list, and find which groups are folder groups with tagGroups_list (allowedRelations contains "project"). A null folderId is the uncategorized bucket. Counts root projects only, since folders group roots and phases follow their parent. Read-only. |
Projects_get
Tools
| projects_get | Get one project by id — name, type, client, dates, description and price. |
Projects_list
Tools
| 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_profitability
Tools
| projects_profitability | THE PER-PROJECT RESULT — what a project earned against what it cost. This is the number a services or development business is usually trying to see, and the one every other project tool feeds. Pass the project id from projects_list. Requires ROLE_ACCOUNT_MANAGER. Read-only. |
Project Templates_get
Tools
| projectTemplates_get | Get one project template by id, including its full structure document. projectTemplates_list finds the id. Read this before projectTemplates_update — the structure is written WHOLE, so an update must send the complete document, not a fragment. Read-only. |
Project Templates_list
Tools
| projectTemplates_list | List the org's project templates — reusable blueprints of a project, its phases, its task lists and its tasks. Reach for this BEFORE projects_create when the same shape of project is set up repeatedly (an engagement type, an audit, an onboarding): instantiating a template builds the whole tree in one call, where projects_create makes an empty project you then have to fill by hand. The row flagged isDefault is the org's built-in template, applied to a project created with no template chosen. Read-only. |
Resource Request Candidates_get
Tools
| resourceRequestCandidates_get | One recruitment candidate by id. The id comes from resourceRequestCandidates_list. Needs ROLE_HR_MANAGER. Read-only. |
Resource Request Candidates_list
Tools
| resourceRequestCandidates_list | The candidates put forward against hiring requests — people in a recruitment pipeline, not employees available for allocation. Filter by the request id from resourceRequests_list. Needs ROLE_HR_MANAGER. Read-only. |
Resource Requests_get
Tools
| resourceRequests_get | One hiring request by id, with its position and status. Get the id from resourceRequests_list. HR/recruitment, not resourcing allocation. Needs ROLE_HR_MANAGER. Read-only. |
Resource Requests_list
Tools
| resourceRequests_list | Open hiring requests — a request to recruit for a position, in the HR domain. Despite the name this is NOT resourcing allocation demand: it is recruitment. Returns the collection; resourceRequests_get reads one, and resourceRequestCandidates_list gives the people put forward for it. Needs ROLE_HR_MANAGER. Read-only. |
Resourcing Requests_list
Tools
| resourcingRequests_list | Open resourcing requests — someone asking for a person to be allocated to a project, which is the demand side of resourcing. This is the flow the Resourcing UI's Requests view renders. Do NOT confuse it with resourceRequests_list: that one is HR RECRUITMENT (hiring for a position). Pair it with resourcingRequestsHistory_list for what has already been decided, and resourcingBench_get for who could satisfy a request. Needs the resourcing module and ROLE_RESOURCING_MANAGER. Read-only. |
Resourcing Requests History_list
Tools
| resourcingRequestsHistory_list | What has already happened to resourcing requests — the decision trail (confirmed, declined, changed) behind the open requests in resourcingRequests_list. Reach for it to answer 'was this already asked for and turned down?' before proposing the same allocation again. Needs the resourcing module and ROLE_RESOURCING_MANAGER. Read-only. |
Responsibilities_get
Tools
| responsibilities_get | Get one responsibility by id. |
Responsibilities_list
Tools
| 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. |
Responsibility Employees_get
Tools
| responsibilityEmployees_get | Get one responsibility assignment by id. |
Responsibility Employees_list
Tools
| 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. |
Responsibility Groups_get
Tools
| responsibilityGroups_get | Get one responsibility group by id. |
Responsibility Groups_list
Tools
| responsibilityGroups_list | List responsibility groups / RACI areas — the top-level "Odpowiedzialności" items, each with an accountable person. Individual responsibilities hang underneath them. |
Schedule Employees_get
Tools
| scheduleEmployees_get | One schedule-to-employee assignment by id. The id comes from scheduleEmployees_list. Needs ROLE_SCHEDULES_MANAGER. Read-only. |
Schedule Employees_list
Tools
| scheduleEmployees_list | Which employees are assigned to which working-time schedules. Use it to go from a schedule (schedules_list) to its people, or to find the schedule a given employee follows. Needs ROLE_SCHEDULES_MANAGER. Read-only. |
Schedule Plan_list
Tools
| schedulePlan_list | The schedules in force on ONE given date — pass the date in the path. Reach for it to answer 'who is working today / on this date' without reading every schedule and resolving its ranges yourself. Unlike the other schedule reads this only needs ROLE_USER, so it is the one available to an ordinary employee. Read-only. |
Schedule Ranges_get
Tools
| scheduleRanges_get | One schedule time range by id. The id comes from scheduleRanges_list. Needs ROLE_SCHEDULES_MANAGER. Read-only. |
Schedule Ranges_list
Tools
| scheduleRanges_list | The time ranges that make up working-time schedules — the actual hours a schedule covers. Read the parent with schedules_get first; this expands its ranges. Needs ROLE_SCHEDULES_MANAGER. Read-only. |
Schedules_get
Tools
| schedules_get | One working-time schedule by id, with its ranges and assigned employees. The id comes from schedules_list; scheduleRanges_list and scheduleEmployees_list read its parts. Needs ROLE_SCHEDULES_MANAGER. Read-only. |
Schedules_list
Tools
| schedules_list | Working-time schedules — the shift/working patterns an org defines, NOT project allocation. Use resourcingSchedule_get for who is booked on what; use this for the working patterns themselves. schedules_get reads one by id. Needs ROLE_SCHEDULES_MANAGER. Read-only. |
Stages_get
Tools
| stages_get | Get one deal stage by id. |
Stages_list
Tools
| 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_list
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. |
Tag Definitions_list
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. |
Tag Groups_list
Tools
| tagGroups_list | List tag groups — the containers that organize tag definitions. |
Task Comments_list
Tools
| taskComments_list | List comments on project tasks, oldest first. Filter by task to read one task's discussion. |
Task Lists_list
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_get
Tools
| tasks_get | Get one project task by id — title, project, status, list, assignees, dates and recurrence. |
Tasks_list
Tools
| 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. |
Task Statuses_list
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_list
Tools
| taxGroups_list | List tax groups. Use it to resolve the taxGroup id that taxRules_list filters on and that invoice rows carry. |
Tax Rules_list
Tools
| taxRules_list | List tax rules — the rates and the periods they apply to. Filter by taxGroup. |
Transactions_list
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
Tools
| 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_get
Tools
| workTimes_get | Get a single work-time entry by id — date, minutes, project, notes and the employee it belongs to. |
Work Times_list
Tools
| 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. |
Agreements_create
Tools
| agreements_create | Create an employment agreement for a person. THIS IS THE STEP THAT MAKES SOMEBODY ACTIVE: people_create only makes the record, and a person with no agreement reports isActive false forever — a bulk import therefore lands 100% inactive until this is run for each of them. TWO THINGS BOTH HAVE TO BE TRUE or they stay inactive with no error: the `type` must be a CALCULABLE one (built-ins "agreement", "annex", "termination" are; "list-of-intent" and "work-experience" are not), and the dateFrom/dateTo window must cover today (pass dateTo null for an ongoing contract rather than a far-future date). `employee` is an IRI — /people/<id> from people_list. Types are org-extendable, so run agreements_list on somebody already active to see the codes this org really uses. Requires ROLE_AGREEMENTS_MANAGER. Write. |
Agreements_update
Tools
| agreements_update | Change an existing employment agreement — the way an agreement is ENDED, because the backend exposes no delete on this resource: set `dateTo` to the last day it covers and the person stops being active from then, with the record and its history intact. That is the correct move for a payroll-adjacent row; there is no way to make one disappear and there should not be. Also the way to correct a wrong `type`, `variant` or `positionName` in place rather than stacking a second agreement on the person — TWO agreements do not cancel out, the calculable one keeps them active, so "add a correct one alongside" silently leaves the wrong one in force. `amount`, `amountType` and `billingType` are accepted but the API never returns them, so you cannot read back what you wrote. Requires ROLE_AGREEMENTS_MANAGER. Write. |
Agreement Types_create
Tools
| agreementTypes_create | Add a contract type to THIS org's list, so an agreement can be recorded against something the five built-ins do not cover — "Umowa zlecenie", "Kontrakt B2B", "Użytkownik funkcyjny". This is configuration, not a code change: the list is a per-tenant table, and a custom type needs no translation entry because its `name` renders verbatim in all seven locales. DO NOT SEND `id`: the code is slugged from the name server-side with diacritics folded ("Użytkownik funkcyjny" becomes "uzytkownik-funkcyjny"), and passing an id is refused with 422 "Update is not allowed for this operation". Post the name and read the assigned code back off the response. `calculable` DEFAULTS TO FALSE AND IS SILENT: it decides who counts as employed — the resourcing bench, holiday accrual, the cost and budget base — so a type meant for people who should NOT accrue leave or occupy an FTE is correct at false, and a type meant for real employment MUST set it true or everyone on it reports inactive with no error. Nothing will tell you which you got. `position` orders the dropdown; `isActive` defaults true. There is no update or delete over MCP on purpose — `agreement.type` stores this row's id as a bare string with… |
Attribute Sets_create
Tools
| attributeSets_create | Create a category (name + relationName required; relationName is one of property | project | budget | client, and for an asset category it is the plain string "property" — NOT an IRI). Optional icon from a fixed list (room, parking, building, office, local, desk, monitor and so on) which the UI shows beside the category. LIST FIRST: names are not unique, so a second "Mieszkanie" is accepted and quietly splits the Assets list in two. Requires ROLE_ATTRIBUTES_MANAGER. Write. |
Attribute Sets_update
Tools
| attributeSets_update | Rename a category, change its icon, or move it to another relationName. This is how a category created with a typo gets fixed rather than duplicated. Requires ROLE_ATTRIBUTES_MANAGER. Write. |
Attributes_create
Tools
| attributes_create | Create an attribute definition (name + type required; type is number | string | date | state | period). THE TYPE IS THE DECISION: it is shared by every entity carrying this attribute, so a field created as `string` cannot later total or sort as a number without every existing value being rewritten. Decide it against the values you actually have, not the first one you see. A definition on its own does nothing — bind it to a category with attributeSetAttributes_create, or it never appears anywhere. Requires ROLE_ATTRIBUTES_MANAGER. Write. |
Attributes_update
Tools
| attributes_update | Update an attribute definition — name, type, required, multiple, default or format. Changing `type` on a definition that already has values is the risky one: existing values are not converted. Requires ROLE_ATTRIBUTES_MANAGER. Write. |
Attribute Set Attributes_create
Tools
| attributeSetAttributes_create | Bind an attribute definition to a category (attributeSet + attribute, both IRIs). THIS IS WHAT MAKES AN ATTRIBUTE APPEAR: without the binding a value can be written successfully against an entity and will never show in the UI — a failure with no symptom. Requires ROLE_ATTRIBUTES_MANAGER. Write. |
Attribute Set Attributes_delete
Tools
| attributeSetAttributes_delete | Unbind an attribute from a category. The definition and any values survive; they simply stop being shown for that category, which makes this look like data loss when it is not. Requires ROLE_ATTRIBUTES_MANAGER. Write. |
Attribute Entity Values_create
Tools
| attributeEntityValues_create | Set an attribute value on one entity (attribute + value required). `relation` IS AN IRI — "/properties/7", not the word "property": the backend resolves it and derives the relation name from the resource class, so passing a bare name throws. (`relationId` takes a plain id and still works, but it is deprecated in favour of the IRI.) The attribute must already be BOUND to that entity's category or the value is stored and never displayed. Requires ROLE_ATTRIBUTES_MANAGER. Write. |
Attribute Entity Values_update
Tools
| attributeEntityValues_update | Change one attribute value in place, by its id. Use this rather than creating a second value for the same (entity, attribute) pair — nothing enforces uniqueness, so a duplicate is accepted and the UI shows one of them. Requires ROLE_ATTRIBUTES_MANAGER. Write. |
Attribute Entity Values_delete
Tools
| attributeEntityValues_delete | Remove an attribute value from an entity. The definition and the binding survive; only this entity's value goes. Requires ROLE_ATTRIBUTES_MANAGER. Write. |
Departments_create
Tools
| departments_create | Add a department, so people can be filed under it. `name` is required (up to 128 characters) and is UNIQUE across the org; `code` is optional (up to 64) and is ALSO unique — the short form an org already uses in its own spreadsheets (CEO, TECH, PROC). `manager` is an optional employee IRI from people_list. LIST FIRST AND EXPECT COLLISIONS: because both name and code are unique, re-posting a department that already exists FAILS rather than being idempotent, so an import that assumes create-per-row will stall the first time it meets a department the org already carries — typically one left over from a trial. Reconcile that row with departments_update instead of creating around it. THERE IS NO DELETE: the backend exposes no delete on a department, so a wrong name or code is corrected in place with departments_update and never removed. Requires ROLE_EMPLOYEES_MANAGER. Write. |
Departments_update
Tools
| departments_update | Rename a department, give it a code, or set its manager. This is the tool that makes a department import possible rather than merely convenient: `name` and `code` are both unique, so a department the org already has — the single "HR" row a proof-of-concept tends to leave behind — cannot be created again, and the real list is reached by CORRECTING that row rather than colliding with it. Only the fields you send change, so passing `code` alone leaves the name intact. `id` is the numeric id from departments_list; `manager` is an employee IRI from people_list. THERE IS NO DELETE, which makes this the whole repair story: a department created with a typo is fixed here, and one that should not exist can only be renamed, not removed. Requires ROLE_EMPLOYEES_MANAGER. Write. |
Locations_create
Tools
| locations_create | Create a location (name required; optional officeOpenHour/officeCloseHour as seconds past midnight). Use the real address rather than a project or investment name — this is what someone standing in front of the asset needs, and the project name is already carried elsewhere. Requires ROLE_LOCATIONS_MANAGER. Write. |
Locations_update
Tools
| locations_update | Rename a location or change its office hours. Requires ROLE_LOCATIONS_MANAGER. Write. |
Clients_import
Tools
| clients_import | Load MANY clients in one call, keyed on `externalRef` — the tool for bringing a customer or buyer list across from another system, where clients_create would be one round trip per person. Rows reconcile against the org: an unknown externalRef creates, a known one updates in place, a matching row is skipped, so re-running changes nothing. The ref is stored as `externalPaymentCustomerId`, the only external-reference column a client has, and `clients_list` filters on it. DO NOT match clients by name instead — a buyer list is full of shared surnames and joint purchases. Each result carries `counterpartyId`, which is what contracts_import and contracts_create need. Two traps the schema cannot express: a `tin` is REJECTED without a `tinCountry`, and a contact row needs an e-mail, so a phone number alone cannot create one. PASS dryRun:true FIRST on a real onboarding load. Max 500 rows. Requires ROLE_CLIENTS_MANAGER. Write. |
Contracts_import
Tools
| contracts_import | Load MANY contracts in one call, keyed on `name` — the agreement number. Unlike a client or an asset, a contract has NO external-reference column, so the name IS the idempotency key; a batch containing the same name twice is REFUSED WHOLE rather than updating one contract twice, because a duplicate number means the source is wrong. `counterpartyExternalRef` resolves the buyer through the same ref clients_import was given, so the two compose: import the clients, then the contracts, without ever handling a numeric counterparty id — a ref matching no client fails that row rather than creating a partyless contract. `direction` is "out" (we sell) or "in" (we buy); the column has no server-side constraint, so a wrong word is stored and the contract then matches no filter anywhere. PASS dryRun:true FIRST. Max 500 rows. Requires ROLE_CONTRACTS_MANAGER. Write. |
Assets_import
Tools
| assets_import | Load MANY assets in one call, keyed on `assetCode` — the tool for bringing an inventory across from another system, where assets_create would be one round trip per record. Rows reconcile against the org: an unknown assetCode creates, a known one updates in place, a matching row is skipped, so re-running changes nothing and a half-finished run is safe to repeat. `parentAssetCode` nests a row under another BY ITS CODE, resolved against the org and against earlier rows of the same batch; a parent that never resolves fails that row rather than silently orphaning it. THREE FIELDS MAKE THE RECORD READABLE rather than a bare name: `attributeSetName` is the category the UI shows as Typ zasobu and the list groups by, `locationName` is where the thing physically is, and `attributes` is a {name: value} map for area, floor, price and anything else the source carries. All three are resolved BY NAME — the category, the location, the attribute definitions and their bindings are found or created for you, so a caller never handles one of those IRIs, and names are matched case-insensitively so "Mieszkanie" and "mieszkanie " cannot split the list in two. `attributes` needs a category to hang off,… |
Assets_create
Tools
| assets_create | Create an asset (name + status + bookingType required; status = in-stock | damaged | sold, bookingType = minutes | days | single-days | permanently). bookingType is required even when the asset is never booked — pass "permanently" for something that is not lent out, and leave bookingAllowed false. Two fields carry the structure: `parent` nests an asset under another (a unit under a building, a monitor under a desk), and `attributeSet` sets the category the Assets list groups by, which is also where custom attributes such as area or floor live. `assetCode` is a UNIQUE cross-system handle — use it to hold the id this asset has in the system of record it was imported from, so a re-import updates rather than duplicates. Requires ROLE_PROPERTIES_MANAGER. Write. |
Assets_update
Tools
| assets_update | Update an asset by id — name, status, category, parent, assetCode, serial number, dates, location or booking settings. This is how an asset moves from in-stock to sold. Note the status vocabulary is in-stock | damaged | sold and has NO reserved state, so a hold has to be modelled some other way. Requires ROLE_PROPERTIES_MANAGER. Write. |
Asset Meters_update
Tools
| assetMeters_update | Update an asset meter — its label, utility type, unit, or active state. Use it to retire a meter from metering (e.g. a utility now billed straight from the invoice) without deleting its reading history. Requires ROLE_PROPERTIES_MANAGER. Write. |
Asset Bookings_create
Tools
| assetBookings_create | Assign an asset to a person or a project. `property` is the asset IRI (/assets/{id}) and is required. Name the holder ONE of three ways: `relation` with a single IRI (/people/{id} for a person, /projects/{id} for a project), or `relationName` (employee | project) plus `relationId`, or the `employee` / `project` IRI field directly. Exactly one holder must resolve — naming neither is refused with "Employee or Project must be set." and naming both with "Employee and Project cannot be set at the same time." TWO THINGS THAT ARE NOT IN THE SCHEMA AND WILL 422 YOU: the asset must already be reservable (`bookingAllowed: true` — set it with assets_update), a business rule enforced for EVERY caller including a manager, refused with "This asset is not reservable."; and the asset's own `bookingType` (minutes | days | single-days | permanently) is what makes sense of `duration` / `endDate` — a space dedicated to one person indefinitely is `permanently` with a `startDate` and no end. Concurrent bookings on one asset are serialised server-side, so an overlap is refused rather than double-booked. Requires ROLE_PROPERTY_BOOKINGS_MANAGER to book on someone else's behalf. Write. |
Asset Bookings_update
Tools
| assetBookings_update | Update an existing asset booking — its dates, duration, billing amount/currency, or metered-consumption share. `relationName` and `relationId` are required by the payload, so send the holder the booking already has unless you are deliberately moving it. To end an assignment use assetBookings_cancel, not an endDate in the past. Requires ROLE_PROPERTY_BOOKINGS_MANAGER. Write. |
Asset Bookings_cancel
Tools
| assetBookings_cancel | Release an asset — the way an assignment ends, and the closest thing this resource has to a delete (there is no delete operation). Takes the booking id and a `cancelReason` of 3-255 characters; the booking is kept and stamped with `cancelledAt` so the history survives, and the asset becomes free for the next holder. This is the call to make when an employee leaves: assetBookings_list filtered by `employee` finds what they hold, and this releases each one. Requires ROLE_PROPERTY_BOOKINGS_MANAGER. Write. |
Work Times_log
Tools
| workTimes_log | Log a work-time entry for the connected Flowtly user (date, durationMinutes, project, notes). THE NOTE MUST CLEAR THE SERVER’S THIN-DESCRIPTION CHECK, which a batch backfill hits repeatedly: it needs EITHER about 32 characters (the exact floor is a per-organisation setting, and an org may set it to 0 to switch the check off) OR a “#” ticket reference OR an http(s) link — any one suffices. “Flowtly – Scallier” is refused; “Flowtly – Scallier #FLOW-123” is not. The 422 names propertyPath `description`, which is the server’s name for the field this tool calls `notes`. Write. |
Tasks_create
Tools
| tasks_create | Create a project task (title + project required; optional status, list, assignees, dueAt, priority). Write. |
Tasks_update
Tools
| tasks_update | Update a project task by id — change status (incl. mark done), assignees, dueAt, title, etc., or MOVE the task to another project by passing `project` (re-parenting; the task list is cleared unless you also name a `list` in the target project, because a list belongs to one project). Write. |
Task Comments_create
Tools
| taskComments_create | Add a comment to a project task (task id + content). Write. |
Suppliers_create
Tools
| suppliers_create | Create a new supplier/contractor record (name, tinType, costGroup required). Write. |
Suppliers_update
Tools
| suppliers_update | Update a supplier/contractor's details (name, tax id, payment terms, etc.) by id. Write. |
People_create
Tools
| people_create | Create a person/employee record (firstname + lastname required; optional companyEmail, contactEmail, contactPhone). Write. |
People_update
Tools
| people_update | Update a person/employee record by id (name, companyEmail, contactEmail, contactPhone, etc.). Write. |
People_delete
Tools
| 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. |
Cost Groups_create
Tools
| costGroups_create | Create a cost group / cost center (name + type required). Write. |
Cost Groups_update
Tools
| costGroups_update | Update a cost group / cost center's name or type by id. Write. |
Tag Groups_create
Tools
| tagGroups_create | Create a tag group (name required) to organize related tag definitions. Also how you create a PROJECT FOLDER container: pass allowedRelations: ["project"] and the group's definitions become folders in the projects list. A group with empty allowedRelations is universal and is NOT treated as a folder. Write. |
Tag Definitions_create
Tools
| tagDefinitions_create | Create a tag definition (name, level, tagGroup required) within a tag group. When the group's allowedRelations contains "project", each definition here IS a project folder — this is the tool that creates one. Write. |
Clients_create
Tools
| clients_create | Create a new client record (name, country, currency, status, tinType required). Write. |
Clients_update
Tools
| clients_update | Update a client record by id. Write. |
Client Contacts_create
Tools
| clientContacts_create | Create a contact person for a client (client, type, name, email required). Write. |
Bank Accounts_create
Tools
| bankAccounts_create | Create a bank account (type, name, currency, defaultImportFormat required). Write. |
Bank Accounts_update
Tools
| bankAccounts_update | Update a bank account by id. Write. |
Banks_create
Tools
| banks_create | Create a bank — the institution a bank account belongs to, not the account itself (that is bankAccounts_create). Write. |
Banks_update
Tools
| banks_update | Update a bank by id. This is also how a bank is hidden and un-hidden: set `hidden` true to retire one from the pickers without deleting it, false to bring it back. There is no separate archive tool because the API has no archive action for a bank — the flag is the mechanism. Write. |
Counterparty Bank Accounts_create
Tools
| counterpartyBankAccounts_create | Attach a bank account to a counterparty (counterparty + accountNumber). Write. |
Contracts_create
Tools
| contracts_create | Create a contract. Write. |
Contracts_update
Tools
| contracts_update | Update a contract by id. Write. |
Contracts_delete
Tools
| contracts_delete | Delete a contract by id. Write. |
Tax Groups_create
Tools
| taxGroups_create | Create a tax group (name + type required). Write. |
Tax Groups_update
Tools
| taxGroups_update | Update a tax group's name or type by id. Write. |
Tax Rules_create
Tools
| taxRules_create | Create a tax rule. Write. |
Tax Rules_update
Tools
| taxRules_update | Update a tax rule by id. Write. |
Configs_update
Tools
| configs_update | Update an organization config value by id (type + name required; permission is enforced per config key by the backend). Write. |
Organization Addresses_update
Tools
| organizationAddresses_update | Update the organization's SUBSCRIPTION address record (id required; send only the fields you are changing). THIS IS THE RECORD THE MAIL FOOTER RENDERS FROM: the footer's {{organizationAddress}} is composed as "street, postCode city" from here, NOT from the organization-billing-* config keys that invoices and KSeF use as the seller address. The two stores drift, and the footer reading this one is a known defect — so when a signature shows an address the customer swears they corrected, they corrected the billing keys and this is the record still holding the old value. `street` is a single free-text column that must carry the building number too: the NIP/GUS lookup fills only the street name and silently discards the building and flat number, which is why addresses here read "ul. Example" with no number. Write the full "ul. Example 8/12" to repair it. READ FIRST with organizationAddresses_list and compare against configs_get on organization-billing-street before writing, so you copy the customer's own maintained value rather than inventing one. Requires ROLE_BILLINGS_MANAGER. Write. |
Permission Groups_create
Tools
| permissionGroups_create | Create a permission group (name required; roles = list of ROLE_* strings it grants). Write. |
Permission Groups_update
Tools
| permissionGroups_update | Update a permission group's name, description, or granted roles by id. Write. |
People_invite
Tools
| people_invite | Give an existing person a LOGIN: creates a pending organization invitation and emails it to them, in the org's configured UI language. This is the step people_create and people_setPermissionGroups do NOT do — a person with permission groups still cannot sign in until they are invited and accept. Requires the person's email; fails if they already have a login. Onboarding order: people_create (record) -> people_invite (login) -> people_setPermissionGroups (rights). Write. |
People_set Permission Groups
Tools
| people_setPermissionGroups | Set (replace) a person's WHOLE permission-group set by numeric group ids (see permissionGroups_list — e.g. the "Business Owner" group grants ROLE_ADMIN): pass every group they should end up with, and [] removes all of them. Grants access; does NOT create a login or email the person — that is people_invite. THE TRAP: giving someone their FIRST group moves them onto the computed model, where roles come from groups and per-person overrides, and a role granted to them by hand outside that model disappears in the very same call — a ROLE_ADMIN handed to one person is exactly the kind this removes. It cuts the other way too: clearing their last group moves them back off it and makes those older roles reappear. The overridesAdded/overridesRemoved lists say nothing about any of this; they describe overrides and stay empty while effective access changes. So the response reports the difference between the roles the person held before this call and after it, as rolesLost and rolesGained — that is the pair to read once the call returns. rolesLost null (not []) means the snapshot taken before the write could not be read and the delta is UNKNOWN, with the reason in roleDeltaUnavailable: the… |
People_set Role Overrides
Tools
| people_setRoleOverrides | Set (replace) the roles ONE person gets on top of — or has taken away from — their permission groups. Reach for a group first (people_setPermissionGroups): groups are the intended abstraction and scale to more than one person, so use an override only where a single individual genuinely differs from every group. REPLACES both lists wholesale, so read people_getPermissions first and pass back every override they should keep; omitting a list clears it. Roles are ROLE_ constants — permissionGroups_list shows the ones this org already uses. A role in both added and removed is refused rather than guessed at. Returns the same resolved snapshot as people_getPermissions, so you can confirm the result without a second call. Does NOT create a login — see people_invite. Requires ROLE_ROLES_MANAGER. Write. |
Holiday Days Limits_create
Tools
| holidayDaysLimits_create | Give a person an allowance of one leave type, effective from a date. `seconds`, NOT days (#3763): an 8h day is 28800, so 21 days is 604800 and an overtime balance of 2h30 is 9000 — a figure that had nowhere to go while this was stored in whole days. `employee` and `holidayType` are IRIs (people_list and holidayTypes_list supply them); `variant` is the contract type the allowance belongs to (uop, b2b, uz, uod). To CORRECT an existing balance, add a row with a later dateFrom rather than editing the old one — the row in force is the latest one whose dateFrom has arrived, so history stays intact and a correction can be entered before it takes effect. (employee, holidayType, variant, dateFrom) is unique, so re-posting the same day replaces nothing and fails. Requires ROLE_HOLIDAYS_MANAGER. Write. |
Holiday Days Limits_update
Tools
| holidayDaysLimits_update | Fix a row that was entered wrongly — a typo in the amount, the wrong variant. Amounts are in SECONDS (#3763). This is NOT how you record a balance CHANGING over time: for that, holidayDaysLimits_create a new row with a later dateFrom, which preserves what the previous balance was and when. Editing in place rewrites history and makes the old figure unrecoverable. holidayDaysLimits_list finds the id. Requires ROLE_HOLIDAYS_MANAGER. Write. |
Holiday Requests_cancel
Tools
| holidayRequests_cancel | Cancel a leave request — use it to clear a request that should never be acted on, such as a row left behind by a trial, a test, or someone who has left. TWO THINGS THAT SURPRISE PEOPLE. (1) IT DOES NOT DELETE THE ROW: the backend sets status to `canceled` rather than removing the row. BUT A CANCELLED REQUEST DISAPPEARS FROM holidayRequests_list — verified on production: afterwards neither the unfiltered list nor status=canceled returns it. So you cannot read back what you cancelled and there is no undo through the MCP; be sure of the id before calling. (2) IT IS NOT THE SAME AS REJECTING. Rejecting records a decision — it writes an approval-log entry naming you and MAILS THE EMPLOYEE that their leave was refused — whereas cancelling notifies only HR, and only when `notify-hr-managers-of-leave-activity` is on for the org. For a row that was never a genuine application, cancel is the honest and quieter one. ONLY WORKS ON A PENDING (`requested`) REQUEST when you are not its owner: an accepted request has already produced a Holiday that this does not remove, so cancelling one would leave a booked absence behind a request reading `canceled`. Requires ROLE_HOLIDAYS_MANAGER for someone… |
Holidays_create
Tools
| holidays_create | Record leave that a person is actually taking — the booked absence itself, not the entitlement (holidayDaysLimits_create) and not a pending application (holiday requests, which still need approving). What this writes is already-agreed time off, so it appears in holidays_list straight away and needs no approval step. `employee` is an IRI from people_list; `type` is a holidayTypes_list id. `dateFrom`/`dateTo` inclusive, and one call covers a whole range rather than a row per day. Two things bite: a type whose `descriptionRequired` is true (read holidayTypes_list first — `vacations` commonly is) REJECTS a create with no `description`; and `pick-up-day` is time already owed, so it does NOT consume the annual allowance the way `vacations` does — filing a day given back for a Saturday public holiday as `vacations` silently eats a day of someone's entitlement. Check holidays_list for the same person and dates before creating: this endpoint will happily record the same absence twice. EVERY CREATE MAILS THE EMPLOYEE, at their own company address, to say the absence was added — so loading a year of history someone already lived through arrives in their inbox row by row, and for staff who… |
Holidays_delete
Tools
| holidays_delete | Remove a booked absence outright — the row is deleted, unlike holidayRequests_cancel which only flips a request's status. Use it to clear absences that should never have counted: demo or test rows left by a trial, or ones orphaned when their employee was deleted (people_delete detaches absences rather than removing them, so they survive with an empty employee name). THIS MOVES REAL NUMBERS: a booked absence is `payrollEligible` and consumes the person's entitlement, so deleting one changes their leave balance — the point when clearing test data, and a data-loss bug when the row was genuine. No undo, no notification. Read holidays_list first and be certain the row is not real history: a description in the org's own language, or dates matching an actual absence, usually means it is. Requires ROLE_HOLIDAYS_MANAGER. Write. |
Holiday Types_create
Tools
| holidayTypes_create | Add a leave type the org does not yet offer — a sabbatical, unpaid childcare, a training day — so absences can be booked against it with holidays_create and an allowance granted with holidayDaysLimits_create. `name` (3–64 characters) is what people pick from when booking; `color` and `icon` are how it reads in the calendar; `reducesWorkingTime` false marks time off that does NOT lower the month's expected hours; and `descriptionRequired` true makes the type demand a reason, which holidays_create then enforces — see that tool for what it rejects. `status` defaults to `active`, so a type created without thinking about it is offered to everyone immediately. READ holidayTypes_list FIRST: types are org-wide, and THERE IS NO DELETE — a duplicate or a misspelt name can only be hidden again by setting status to inactive with holidayTypes_update, and it keeps every absence booked against it in the meantime. Requires ROLE_HOLIDAYS_MANAGER. Write. |
Holiday Types_update
Tools
| holidayTypes_update | Change a leave type, and above all TURN ONE BACK ON. `status` flips between `active` and `inactive`, and an inactive type is refused by holidays_create — so recording historic leave against a type the org has since retired starts here, and this is what unblocks a leave-history import rather than sending someone into the app UI. DEACTIVATING IS NOT DELETING, and there is no delete: absences already booked keep an inactive type and still read with it in holidays_list, so inactive means only 'not offered for new bookings'. THE TRAP THAT FOLLOWS FROM THAT: reactivate `vacations` to load last year's absences, forget to set it back to `inactive`, and you have not merely finished an import — you have changed what the organisation offers today, because every employee booking leave now sees that type on the list again. Set it back in the same session you imported. `descriptionRequired` also reaches into holidays_create, which refuses a booking with no description once it is on; turning it on leaves absences already recorded alone. `id` is the string id from holidayTypes_list (`vacations`, `not-paid`), and only the fields you send change. Requires ROLE_HOLIDAYS_MANAGER. Write. |
Projects_create
Tools
| 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
Tools
| projects_update | Update a project by id (name, type, dates, description, etc.). Write. |
Project Members_create
Tools
| projectMembers_create | Put a person ON a project (employee + project IRIs required, e.g. "/people/204" and "/projects/243"; optional position = employee|tech-lead|account-manager|viewer, default employee). THIS IS THE ACCESS CONTROL, not a label: a person who is not a member does not see the project at all — it is missing from their Projects list and they cannot log time against it — so this is the tool that restores someone locked out of a project. POSITION IS NOT COSMETIC: a user holding a project-scoped role sees only the projects where their membership position matches it — ROLE_PROJECTS_LEAD matches tech-lead, ROLE_PROJECTS_VIEWER matches viewer — so giving a project lead an `employee` row leaves them just as blind as no row at all. Membership does NOT cascade: putting someone on a parent folder gives them nothing on the projects underneath it, so a folder tree needs one call per project. The unique key is (employee, project, position), which means positions stack rather than replace — a person can hold employee AND tech-lead on the same project as two separate rows, and adding tech-lead to someone who is already an employee there does not remove or upgrade the employee row (use… |
Project Members_update
Tools
| projectMembers_update | Change an existing membership's position by id (employee|tech-lead|account-manager|viewer) — get the id from projectMembers_list or the projectMembers array on projects_get. Use this to promote or demote IN PLACE; use projectMembers_create to add a second, additional position alongside the one already held. Changing a position can REVOKE sight of the project for someone whose role is project-scoped (a ROLE_PROJECTS_LEAD demoted from tech-lead to employee stops seeing it). Cannot move a membership to another person or project — delete and recreate for that. Requires ROLE_PROJECTS_MANAGER. Write. |
Project Members_delete
Tools
| projectMembers_delete | Take a person OFF a project by membership id — find it with projectMembers_list or in projects_get's projectMembers array. This REVOKES ACCESS: once the last membership row for that person on that project is gone, the project disappears from their view and they can no longer log time against it, which is exactly how a project silently vanishes for someone. Hours already logged are NOT deleted and stay on the project; the person simply can no longer see or add to them. Deleting one position leaves any other position the same person holds on the same project intact. Requires ROLE_PROJECTS_MANAGER. Irreversible (recreating makes a new row and re-notifies), high-impact. Write. |
Project Templates_create
Tools
| projectTemplates_create | Create a reusable project blueprint from a structure document (version, project, phases, and their lists/tasks). Offsets inside it are RELATIVE — startOffsetDays and durationDays are counted in days from the startDate given at instantiate time, so one template serves every future start. The project.name in the structure is a placeholder; override it per customer when instantiating. The structure is validated server-side against the schema for its declared version, and a violation names the offending JSON pointer. Write. |
Project Templates_update
Tools
| projectTemplates_update | Update a project template by id. The structure column is stored and replaced WHOLE, never merged — send the complete document or the parts you omit are gone. Read the current one with projectTemplates_get first. Changing a template does NOT touch projects already instantiated from it; there is no back-propagation. Write. |
Project Templates_delete
Tools
| projectTemplates_delete | Delete a project template by id. Soft delete, and it does NOT touch projects already created from the template — those are ordinary projects and live on. Write. |
Project Templates_instantiate
Tools
| projectTemplates_instantiate | Build a real project from a template — the project, its phases, its task lists and every task, in ONE atomic call. startDate is required and is the anchor every startOffsetDays in the template resolves against. Pass name to override the template's placeholder project name, and client to attach the new project to a customer: instantiating twice against the SAME client is how one customer ends up holding several engagements, each its own project. Returns the created project. Write. |
Projects_archive
Tools
| projects_archive | Archive a project by id — the way to retire a project that cannot be deleted because it has logged time, invoices or budgets hanging off it. Reversible with projects_unarchive. Prefer this over back-dating dateTo, which only makes a project look finished. Write. |
Projects_unarchive
Tools
| projects_unarchive | Restore an archived project by id, undoing projects_archive. Write. |
Responsibility Groups_create
Tools
| 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. |
Responsibility Groups_update
Tools
| responsibilityGroups_update | Update a responsibility group by id (name, description, responsibleEmployee = employee id or IRI). Write. |
Responsibilities_create
Tools
| 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
Tools
| responsibilities_update | Update a responsibility by id (name, description, parent, responsibilityGroup = group id or IRI). Write. |
Responsibility Employees_create
Tools
| 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. |
Responsibility Employees_update
Tools
| responsibilityEmployees_update | Update a responsibility assignment by id (percentage, targets, description). Write. |
Responsibility Employees_delete
Tools
| responsibilityEmployees_delete | Remove an employee's assignment from a responsibility by id. Write. |
Leads_create
Tools
| leads_create | Create a lead (outbound/inbound prospect target; companyName, source, owner, linked client optional). A new lead is always status=open — status is not settable here, and moves only through leads_convert, leads_lose and leads_reopen. Write. |
Leads_update
Tools
| leads_update | Update a lead by id (company, website, source, owner, linked client, stage, doNotContact). NOT status or lostReason: those are refused by the entity and silently ignored by this endpoint, so closing a lead needs leads_lose (with a lostReasonId) and undoing that needs leads_reopen. Moving `stage` walks the funnel; it does not close the lead. Write. |
Leads_delete
Tools
| leads_delete | Delete a lead by id (soft delete). Write. |
Leads_convert
Tools
| 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. |
Leads_lose
Tools
| leads_lose | Close a lead as LOST — sets status=lost and stamps closedAt. REQUIRES lostReasonId, the `id` of a leadLostReasons entry (run leadLostReasons_list first; it is a picklist, so free text is refused with 422). This is the ONLY way to record a lead as lost: leads_update ignores status, and doNotContact means "never contact again", which is a different and much stronger statement than "we did not win this one". It does NOT move the lead's stage — LeadStage has no terminal flag, so the lead keeps its funnel position and leads_reopen can restore it exactly. Write. |
Leads_reopen
Tools
| leads_reopen | Undo leads_lose — sets status back to open and clears closedAt and the lost reason. The stage is untouched, so the lead resumes exactly where it was. Reach for this when a lead was closed against the wrong record or the prospect came back. Write. |
Lead Activities_create
Tools
| leadActivities_create | Log ONE outreach touch on a lead — an invite sent, an invite accepted, a message, a reply, a call, a follow-up (lead + type + occurredAt required; channel, contact, body optional). THIS is where a prospect's outreach history belongs: a crmNote is freeform commentary, an activity is the structured, filterable touch-log the prospecting queue timeline renders. Do NOT narrate touches into a note. type: invite_sent | invite_accepted | message_sent | reply_received | call | meeting | follow_up | …; channel: linkedin | email | phone | …. Write. |
Lead Activities_update
Tools
| leadActivities_update | Update a logged outreach activity by id (type, channel, occurredAt, body). Write. |
Lead Activities_delete
Tools
| leadActivities_delete | Delete a logged outreach activity by id. Write. |
Lead Contacts_create
Tools
| leadContacts_create | Add a contact person to a lead (lead + name required; email, phone, role, linkedinUrl, isPrimary optional). A contact's LinkedIn URL belongs in linkedinUrl, NOT in a crmNote. Write. |
Lead Contacts_update
Tools
| leadContacts_update | Update a lead contact by id — e.g. set linkedinUrl / email / phone once you find them. Write. |
Lead Contacts_delete
Tools
| leadContacts_delete | Delete a lead contact by id. Write. |
Deals_create
Tools
| deals_create | Create a deal/opportunity. Required: title, stage (from stages_list), and an ANCHOR — at least one of client or lead. A deal with neither is refused with 422 “A deal must reference a client or a lead.”, so anchor a prospect you have no customer record for to its lead (`/leads/<id>` from leads_list) rather than inventing a client; pass client (`/clients/<id>` from clients_list) once there is one. Setting both is allowed. Optional: amountMinor, currency, expectedCloseDate, owner, contact. Creating straight into a won stage additionally requires client — a lead-only deal cannot be won. Write. |
Deals_update
Tools
| deals_update | Update a deal by id (title, stage, amountMinor, currency, expectedCloseDate, owner, contact, client, lead). Moving the stage is logged automatically. The anchor rule from deals_create still applies to the result, so you cannot clear the only client or lead a deal has — swap one in first. Moving a deal into a won stage requires client: attach the customer here (or run leads_convert) before winning a lead-only deal. Write. |
Deals_delete
Tools
| deals_delete | Delete a deal by id (soft delete). Write. |
Deals_win
Tools
| deals_win | Mark a deal won — moves it to a won stage and stamps it closed; optional contractId links an existing contract. BACKFILLING A HISTORICAL WIN: pass optional closedAt (ISO-8601, e.g. “2026-05-07” or a full timestamp) to record the date it ACTUALLY closed. Omit it and the server stamps now, which puts an old deal in this month’s “won this month” figure — so set it whenever you are entering a deal that closed before today. It may not be in the future (422), and it MAY be earlier than the deal’s own createdAt: a deal created today and closed in May is the normal shape of a correct backfill, not an error. The deal must ALREADY reference a client: winning a lead-only deal is refused with 422 “Attach a customer before marking this deal Won.”, because there is no customer to bill. Turn the lead into one with leads_convert, or set client with deals_update, then win. Write. |
Deals_lose
Tools
| deals_lose | Mark a deal lost — requires lostReasonId (from dealLostReasons_list); optional lostReasonNote. BACKFILLING A HISTORICAL LOSS: pass optional closedAt (ISO-8601) to record the date it ACTUALLY closed, exactly as deals_win does. Omit it and the server stamps now. It may not be in the future (422), and may be earlier than the deal’s createdAt. Write. |
Deals_reopen
Tools
| deals_reopen | Reopen a won/lost deal back to open. Write. |
Lead Lists_create
Tools
| leadLists_create | Create an outbound prospecting list (name required). Write. |
Lead Lists_update
Tools
| leadLists_update | Update an outbound list by id. Write. |
Lead Lists_delete
Tools
| leadLists_delete | Delete an outbound list by id. Write. |
Lead List Memberships_create
Tools
| leadListMemberships_create | Add a lead to an outbound list (list + lead required; status optional). Any lastContactedAt you pass is a snapshot that nothing will advance afterwards — log the touch as a lead activity as well, or it stays unqueryable. Write. |
Lead List Memberships_update
Tools
| leadListMemberships_update | Update a lead’s membership in a list — e.g. set outreach status (contacted/replied/bounced). status and lastContactedAt are caller-maintained: what you write stands until someone writes again, and recording lead activities does NOT update them. Write. |
Lead List Memberships_delete
Tools
| leadListMemberships_delete | Remove a lead from an outbound list. Write. |
Work Times_update
Tools
| workTimes_update | Correct one logged work-time entry by id — its date, minutes, project or description. This is how a mis-filed entry is MOVED between projects: workTimes_log only ever creates, so without this a wrong project or a typo in the description is permanent. Read the entry first with workTimes_get. The same thin-description check applies as on workTimes_log: roughly 32 characters — the floor is a per-organisation setting and may be 0, which disables it — OR a “#” ticket reference OR an http(s) link, any one of the three. Write. |
Work Times_delete
Tools
| workTimes_delete | Delete one logged work-time entry by id. For a duplicate or an entry logged against work that never happened — prefer workTimes_update when the entry is real but wrong, so the hours stay in the record rather than vanishing from it. Logged hours feed project financials and utilisation, so a delete quietly changes reported numbers for a past period. Write. |
Payment Schedule Lines_import
Tools
| paymentScheduleLines_import | Load a contract's whole instalment plan in one call, instead of one round trip per line. Built for developer contracts, which are paid in construction tranches — a single sale is six to twelve instalments, and a register of them is hundreds. Each row names its contract BY NAME (for an imported developer contract, its agreement number), a due date, and an amount in MINOR UNITS — grosze, so 5 300,00 is "530000" and "5300" silently books 53,00. Rows reconcile against the lines already there on contract+date+amount+note, so an unknown line is created, an identical one is skipped, and re-running the same batch changes nothing; PaymentScheduleLine has no external-reference column, so that natural key is the reconcile key. A row whose contract name matches nothing, or matches MORE than one contract, is reported failed rather than attached to a guess — putting an instalment on the wrong contract misstates two cashflows at once. Pass dryRun:true first on a real load. Max 1000 rows. Write. |
Payment Schedule Lines_create
Tools
| paymentScheduleLines_create | Add one instalment to a contract's payment schedule — the plan of what is expected to be invoiced or paid, and when. Pass the contract IRI, a date and an amount. This is what clears the missing-payment-schedule problem contracts_get reports on a non-cyclic contract: a one-shot fee still has a schedule, it is simply a single line for the whole amount on the day it falls due. Cyclic contracts are not checked for one, because the system does not auto-generate lines from a cadence. AMOUNT IS IN MINOR UNITS — grosze, not złote: 5 300,00 is "530000", and "5300" silently books a 53,00 line. The API returns them the same way, so read one back with contracts_paymentScheduleLines if you are unsure of the scale. Read the result back with contracts_paymentScheduleLines. Write. |
Payment Schedule Lines_update
Tools
| paymentScheduleLines_update | Change one payment schedule line by id — its date, amount or note. Use it when an instalment slips or is renegotiated, rather than deleting and recreating, so the line keeps any invoice already matched to it. AMOUNT IS IN MINOR UNITS — grosze, not złote: 5 300,00 is "530000", and "5300" silently books a 53,00 line. The API returns them the same way, so read one back with contracts_paymentScheduleLines if you are unsure of the scale. Write. |
Payment Schedule Lines_delete
Tools
| paymentScheduleLines_delete | Remove one payment schedule line by id. Deletes the PLAN, not the money: an invoice or transaction already matched to the line is not affected, but it stops being reconciled against anything. Prefer paymentScheduleLines_update for an instalment that moved. Write. |
CRM Notes_create
Tools
| crmNotes_create | Add a note to a lead or a deal (body + exactly one of lead/deal). Author is the connected user. Write. |
CRM Notes_update
Tools
| crmNotes_update | Update a CRM note’s body by id. Write. |
CRM Notes_delete
Tools
| crmNotes_delete | Delete a CRM note by id. Write. |
Organization Logo_upload
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_upload
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. |
Storage_upload
Tools
| storage_upload | Attach a file to any record Flowtly's generic storage accepts — an ASSET (relationName "property"), a project, a task, a client, a location, a contractor, an invoice, an HR record. This is the only route to an asset PICTURE: uploading with relationName "property" sets the image the app shows for that asset (served as `file` on the asset payload). Property has no image column -- the picture is derived from this table at read time, which is why nothing on the entity hints it exists. It is ONE slot and the newest upload wins, so a second image replaces the first rather than adding to a gallery; both rows stay listed under /assets/{id}/documents. The same holds for location, invoices and transaction-attachments; clients, agreements and candidates instead accumulate every upload under `files`; the rest surface only on their /documents route. `file` is omitted from LIST responses unless the request passes ?include=file, so read one record back to confirm the picture landed. Pass relationName + relationId (the id from that record's list tool; a /assets/7 IRI is accepted and reduced) plus the bytes as base64 with a contentType and filename. SIZE LIMIT: the bytes travel as base64 inside… |
Storage_create Upload Ticket
Tools
| storage_createUploadTicket | Mint a short-lived, single-use ticket for attaching a LARGE file to any record — the way asset PICTURES actually get in, since an image is always past the base64 ceiling. On property/location/invoices/transaction-attachments the newest upload becomes the record's visible image, replacing the previous one; on clients/agreements/candidates uploads accumulate. Use this instead of storage_upload whenever the file is more than a few tens of KB: that tool carries the bytes as base64, which a caller has to emit as text, and a 400 KB JPEG becomes ~533 K base64 characters, far beyond what fits in one response. Pass relationName + relationId plus a filename; you get back an uploadUrl and a ready-to-run curl. Then send the file's RAW BYTES to that URL (curl --data-binary @photo.jpg) — not base64, not multipart — and the response carries the created Storage record. The ticket expires in 15 minutes, works once, and can only file against the one record it names. Write. |
Contract Attachments_create
Tools
| contractAttachments_create | Attach a document to a contract — normally the executed PDF, or an annex (DPA, SLA, price annex) filed alongside it. Pass the bytes as base64 with a fileName and the contract id from contracts_list; `contractId` here is a BARE id, unlike the IRIs contracts_update takes for counterparty and project, though a full /contracts/<id> IRI is accepted and stripped. SIZE LIMIT: the bytes travel as base64 inside this call, so the whole document has to fit in one model response — keep it under roughly 150 KB, and for anything larger use contractAttachments_createUploadTicket instead, which is built for exactly this and has no such ceiling. An executed contract with a signature card is usually well past that (673,617 bytes becomes 898,156 base64 characters, several times what one response can carry), and no error comes back when it does not fit, because the call cannot be emitted at all — the request never reaches the server, so check the file size BEFORE starting rather than discovering it by failing. This is what clears the missing-document problem contracts_get reports, so a contract maintained over the API stops sitting in the app's needs-tidying queue. One signed document can back… |
Contract Attachments_create Upload Ticket
Tools
| contractAttachments_createUploadTicket | Mint a short-lived, single-use ticket for attaching a LARGE document to a contract — the executed PDF, or an annex. Use this instead of contractAttachments_create whenever the file is more than a few tens of KB: that tool carries the bytes as base64, which a caller has to emit as text, and a real signed contract (~700 KB, ~900 K base64 characters) is far beyond what fits in one response. Pass the contract id from contracts_list plus a fileName; you get back an uploadUrl and a ready-to-run curl. Then send the file's RAW BYTES to that URL (curl --data-binary @file.pdf) — not base64, not multipart — and the response is the created attachment. The ticket expires in 15 minutes, works once, and can only attach to the one contract it names. This is what clears the missing-document problem contracts_get reports. Write. |
Incoming Invoices_create
Tools
| 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. |
Invoices_export
Tools
| 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_export Status
Tools
| 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. |
Invoices_import
Tools
| invoices_import | File an ALREADY-ISSUED outgoing (sales) invoice into the org — for bringing invoice history in when onboarding. The external invoice number you pass is preserved verbatim, the buyer is resolved by tax id (created if absent), and the invoice lands as issued WITHOUT rendering a PDF, emailing the client, or submitting to KSeF. Importing a number that already exists is a no-op that reports the existing invoice, so a bulk import is safe to re-run — but that guarantee holds for sequential calls only; two genuinely concurrent imports of the same number can both land. Pass expectedGrossTotal (the gross printed on the source document) and the import is rejected if it disagrees with the total computed from the rows. buyer.tin is required — the buyer is never matched by name. Use invoices_create, not this, to raise a genuine new invoice. Write. Pass dryRun:true to PREVIEW without writing — it reports would-create / would-skip and creates no invoice and no client; run a historical backfill dry first and check the counts before running it for real. |
Invoice Transactions_create
Tools
| invoiceTransactions_create | Record a payment against an outgoing (sales) invoice. `invoice` is an invoice IRI from invoices_list; `date` is when the payment is treated as made. `transaction` is optional — omit it to record settlement with no bank line, which is what you want for historical invoices whose bank statement was never imported. `amount` is optional and defaults to the invoice's outstanding amount. Recording a payment is what stops an issued, past-due invoice being treated as unpaid, so it is also what stops payment reminders being queued for it. Nothing prevents recording two payments against one invoice, so read invoices_get first if you are unsure whether one is already settled. Write. |
Invoice Transactions_update
Tools
| invoiceTransactions_update | Update an existing invoice-payment record by id (from invoices_get's invoiceTransactions, or by paging invoiceTransactions). The most common use: point a payment recorded with no bank line at a transaction you just imported via transactions_importStatement, by setting `transaction` to a transaction IRI/id from transactions_list. THE FOOTGUN: this is a PATCH, but the backend still requires `invoice` and `date` on every call — it does NOT merge in the existing values for you. Read the record first (or already have it from the create call) and resend its `invoice` and `date` unchanged alongside whatever you actually mean to change, or the update is rejected. `transaction` accepts null to unlink a payment from a bank line. `amount` is optional. Write. |
Invoice Transactions_delete
Tools
| invoiceTransactions_delete | Delete a payment record from an invoice by id — read the ids from invoices_get's invoiceTransactions. This removes the RECORD THAT AN INVOICE WAS PAID, not a bank transaction: use it when an invoice carries a payment that should never have existed, the usual case being the same payment booked twice — once by hand and once by the statement import that later matched it. Check invoices_get first and delete the record whose `transaction` is the wrong one (keep the one pointing at the real imported bank line); deleting the last remaining payment makes the invoice unpaid again, which re-arms payment reminders for it. Requires ROLE_INVOICES_MANAGER. Irreversible, high-impact. Write. |
Invoices_create
Tools
| invoices_create | Raise a NEW outgoing (sales) invoice — the tool for billing a client for the first time. Do not confuse it with its two neighbours: invoices_import back-files an invoice that was ALREADY issued elsewhere (onboarding history), and incomingInvoices_create files a supplier's COST document. The invoice lands UNSENT: status is derived from the invoice's log rows and a fresh invoice has none, so nothing is rendered, emailed, or submitted to KSeF by this call — treat the result as a draft to review before issuing. `name` is the invoice number and is yours to choose (max 32 chars) — read invoices_list first and follow the org's existing series rather than inventing one, because nothing here allocates the next number for you. Required: name, type ("invoice"), tinType, issueDate, saleDate, dueDate. Pass `client` (IRI from clients_list) and, for a booking that reconciles later, `contract` (IRI from contracts_list) so the invoice shows up under that contract. Line items go in `invoiceRows` — net unit price, quantity and a tax rate per row; the totals are computed from the rows, not passed in. `bankAccount` (from bankAccounts_list) picks the account printed on the document, and `currency`… |
Invoices_update
Tools
| invoices_update | Correct an outgoing (sales) invoice by id, before or after issuing. The everyday use is fixing a draft raised by invoices_create — a wrong date, a wrong row, a missing contract link — rather than deleting and re-raising it, which would burn an invoice number. Read invoices_get first: this is a PATCH over a document whose totals are derived from its rows, so replacing `invoiceRows` replaces the whole set, and an invoice that has already been sent will not un-send itself because you edited it. Write. |
Incoming Invoices_apply Suggestion
Tools
| 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. |
Incoming Invoices_accept All Suggestions
Tools
| 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. |
Incoming Invoices_check EInvoices
Tools
| 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. |
Resourcing_import Timeline
Tools
| resourcing_importTimeline | Import a resourcing allocation timeline sheet (fetch it via the Drive MCP, pass its CSV verbatim). This is a FULL-REPLACE mirror of the org's Allocation rows for `year`: rows in the sheet are created/updated, and any existing row for that year absent from the sheet is DELETED — not a merge. DRY-RUN BY DEFAULT: an omitted dryRun previews and writes nothing; pass dryRun:false to apply. The report gives `created` / `replaced` plus `unmatchedPeople` / `unmatchedProjects`. TWO THINGS ARE EASY TO MISS: a sheet row whose project does not resolve is SKIPPED while the call still reports success, so a green result can hide a partial import; and a role code the position catalogue does not already hold is CREATED as a new position rather than rejected — see `createdPositions`. Both are called out in `warnings` when they happen; surface that to the user rather than reporting only `created`. A sheet that parses to zero rows is refused (it looks exactly like a bad read about to wipe the whole timeline) unless you pass force:true. Read allocations_list afterwards to see what landed. High-impact. Write. |
Transactions_import Statement
Tools
| transactions_importStatement | Import a bank statement file (e.g. an MT940 .sta file) — pass each file's raw text content verbatim (NOT base64) with a filename. THERE IS NO bankAccount PARAMETER: the backend routes a file by stripping all non-digit characters from your bank accounts' numbers and from the file's bytes, and importing into every account whose digits appear anywhere in the file — so one file can land in several accounts, and a statement for an account that is not set up in Flowtly (or whose number is recorded differently than the bank writes it) imports into none of them, failing with an error that explains exactly why — read that message, it is the only diagnostic this endpoint gives you. On success the response is `{ imported, matching }`: `matching: "in_progress"` means contractor/attachment matching for the new rows is still running after this call returns, so an immediate transactions_list may show rows not yet matched — re-read a little later for the final state. Re-importing the same statement does not create duplicate rows; the importer recognizes transactions it has already seen. Once a statement is in, point an existing no-bank-line payment at one of its rows with… |
Transactions_delete
Tools
| transactions_delete | Delete a bank transaction by id — find it with transactions_list. Reach for this ONLY to undo a bookkeeping mistake that cannot be corrected any other way: a statement imported against the wrong bank account, or rows entered by hand before the real statement arrived and now duplicated by it. A transaction is a record of what the bank did, so deleting one on an imported account makes the ledger disagree with the bank; the backend allows it for ROLE_ADMIN only (a transactions manager can delete solely on cash/manual accounts). BEFORE deleting a suspected duplicate, prove the pair: match the imported row on amount AND invoice number AND counterparty, not amount alone — a payment that arrived after the statement's end date has no counterpart and deleting it destroys the only record of that income. The backend DETACHES rather than deletes what hangs off it: invoice payments survive with their bank line cleared (re-point them with invoiceTransactions_update), attachments and properties are unlinked, while project and employee transaction rows are removed with it. Irreversible, high-impact. Write. |
Organization_whoami
Tools
| organization_whoami | Return the organization this MCP connection is bound to — { orgId, name, slug, userId }. Call it to confirm WHICH tenant you are about to write into before any create/update: the connection is pinned to exactly one org by the token, and writing prospects/records into the wrong org is a real incident. Read-only. |
Resourcing Actuals_get
Tools
| resourcingActuals_get | Reported hours vs the plan, per person per week, over a from/to window — the 'is the team actually on plan?' question, which NO other resourcing tool answers: allocations tell you what was PLANNED, this tells you what was DELIVERED. Returns week columns plus one row per person (planned %, reported %, variance, totals, and a per-project breakdown). reportedPercent null means 'no contract that week' and 0 means 'a contract existed and nothing was reported' — do NOT collapse the two. Pass financials for revenue/cost/margin, which are omitted otherwise. Needs the resourcing module and ROLE_RESOURCING_MANAGER. Read-only. |
Resourcing Bench_get
Tools
| resourcingBench_get | Who is NOT staffed over a from/to window — the bench. Reach for it when asked who to put on a new project or where capacity is going unused; resourcingActuals_get tells you how loaded people are, this tells you who has no load at all. IT DOES NOT KNOW ABOUT LEAVE: freePercent is 100 minus confirmed allocations, nothing else, so someone on three weeks' approved holiday reads 100% free and no field on the response says otherwise. Answering 'who is available' from this alone will put people on projects while they are away — cross-check holidays_active or holidays_list. Needs the resourcing module. Read-only. |
Resourcing Schedule_get
Tools
| resourcingSchedule_get | The planned resourcing schedule over a from/to window — the allocation timeline as the planner shows it. Use it for what is BOOKED going forward; use resourcingActuals_get for what was actually reported against it. Needs the resourcing module and ROLE_RESOURCING_MANAGER. Read-only. |
People_get Permissions
Tools
| people_getPermissions | What a person can actually do, resolved: their permission groups (each with the roles it grants), their per-person overrides, and the effectiveRoles the two combine into. THE way to check whether an access change landed — people_list shows a roles field, but this is the one that explains WHY it holds those roles and which lever to pull to change it. Reach for it before every people_setRoleOverrides call, because that tool replaces the override lists wholesale and this is where you read the current ones. staleOverrides are removed-overrides that no longer match any group-granted role, so they currently do nothing. people_list supplies the id. Requires ROLE_ROLES_MANAGER to view anyone but yourself. Read-only. |
Leads_bulk Import
Tools
| leads_bulkImport | Import many leads in ONE call, each with its contacts, list membership and outreach activities nested — the server creates the lead then threads its id into the children, so you never juggle intermediate IRIs. Idempotent by natural keys (companyName / email / (list,lead) / (type,occurredAt,contact)): safe to re-run and to chunk (≤100 leads/call). This is the bulk path a campaign import should use instead of N leads_create calls. Write. |
Lead Activities_by List
Tools
| leadActivities_byList | Every lead activity on a CAMPAIGN (a lead list), in one call — pass the list's id, IRI, or exact name. leadActivities_list filters by a single lead, so campaign-level reporting otherwise costs one call per member (302 for a list like PZFD); this resolves the list's members and reads their activities in bounded batches instead. Combine with type and occurredAt.after/.before to get the counts people actually ask for: reply rate (type=reply_received), bounce rate (type=bounced), send coverage (type=message_sent). Returns listId, listName, leadCount, and the merged activities sorted by occurredAt. An unknown list is an ERROR, not an empty result — so a mistyped name cannot read as "this campaign had no activity". Ids come from leadLists_list. Read-only. |
Lead Activities_bulk Import
Tools
| leadActivities_bulkImport | Record a whole outbound wave — every message you actually sent — in ONE call, instead of one leadActivities_create per message. Pass an array; each row names its lead (leadCompanyName, matched against an EXISTING lead, or a lead IRI) plus type and occurredAt. Give every row an externalId — the stable per-message id, e.g. the Gmail message id — and the import is idempotent: re-running it, or re-running a wave that was only partly imported, reports duplicates instead of creating them. Rows without an externalId dedupe on (lead, type, occurredAt, contact), the same natural key leads_bulkImport uses, so a wave that first landed through that tool is not duplicated here. Every row gets its own outcome (created | duplicate | error), so one malformed row does not discard the rest of the batch. Does NOT create leads — use leads_bulkImport for that. ≤ 1000 rows/call. Write. |