For AI agents
SplitPop for AI agents
Updated 25 Sept 2026
Looking to use SplitPop in ChatGPT or Claude? The page for AI assistants walks you through it, without the technical detail.
SplitPop speaks the Model Context Protocol. Connect it to Claude, ChatGPT or any other MCP client, and you can read your groups, add an expense from a photo of the receipt, record a payment, invite somebody, decide who gets in and send someone the group link without leaving the conversation you are already having.
An agent acts as you. It sees what you see in the app, under your own role in each group, and it can only do what you agreed to when you connected it.
The endpoint
One address for every client. Everything else — which tools exist, where to sign in, what to ask permission for — the client discovers from it.
https://site.splitpop.app/mcp2026-07-28, 2025-11-25, 2025-06-18, 2025-03-26splitpop 1.0.0How to connect
Every client asks for the same address. The first tool that needs permission sends you to sign in with your SplitPop account.
Claude
Customize, then Connectors, then Add custom connector. Paste the address and sign in when Claude asks. Once added on the web or in the desktop app, it also works in the mobile apps. In a Team or Enterprise organization an owner adds it once under Organization settings, Connectors, and everybody then connects it under Customize, Connectors.
https://site.splitpop.app/mcpClaude Code
Add it once from the terminal, then run the /mcp command inside Claude Code to sign in.
claude mcp add --transport http splitpop https://site.splitpop.app/mcpChatGPT
Turn on developer mode under Settings, Security and login. Then open chatgpt.com/plugins, choose the plus button, give the connection a name, enter the address and create it. Deep research finds SplitPop through the search and fetch tools and cites the group and expense pages it read.
https://site.splitpop.app/mcpCursor
Put this in .cursor/mcp.json, either in your home directory or in a project.
{
"mcpServers": {
"splitpop": {
"url": "https://site.splitpop.app/mcp"
}
}
}VS Code
Put this in .vscode/mcp.json in the workspace.
{
"servers": {
"splitpop": {
"type": "http",
"url": "https://site.splitpop.app/mcp"
}
}
}Windsurf, Cline and everything else
Any client that takes an MCP server as a URL works the same way: give it the address and let it discover the rest. Most of them read the file shape Cursor uses, under their own configuration file. An agent registered with WorkOS signs in through AuthKit's own agent flow instead of a browser: it must ask for the resource https://site.splitpop.app/mcp, so the token it is given names SplitPop as its audience, and it can act for a person only once that person has claimed the registration.
{
"mcpServers": {
"splitpop": {
"url": "https://site.splitpop.app/mcp"
}
}
}What you agree to
The service you sign in with decides which permissions a token can carry, and with the one SplitPop uses today it cannot carry these. It issues exactly one consent, and it is all or nothing: signing in gives the assistant everything on this page, or you do not connect it at all. There is no screen where you tick reading and leave out writing, because the sign-in service has no way to offer one.
So the list below is not a list of boxes to tick. It is what each part of the surface touches, named the way the server names it, so you can read what an assistant is being handed before you hand it over. Every tool further down says which of these it needs.
Consent is not the last word either way. Your own role in a group still decides what you may do in it, and an assistant holding your token can do no more than you can. If the sign-in service ever issues these one at a time, SplitPop asks for them one at a time, and this page will say so.
groups:readRead your groups: people, members, invitations, balances, expenses and payments, and what you are owed across all of them.
groups:writeCreate a group — on its own, or with the same people as another one — and change its name, emoji, display currency, who may join it, how its settle-up plan is worked out, and whether its link is public; and ask to join a group, or to be one of its people.
chat:readRead a group's activity timeline, including what people wrote.
chat:writePost messages in a group, edit and delete your own, react to any, and send one person a reminder of what they owe you, by e-mail and push.
expenses:writeCreate expenses — one at a time, a receipt's several lines together, or again like an earlier one — work them out before recording them, edit one, delete one, and restore one you deleted.
payments:writeCreate, edit, delete and restore payments, in any currency, and record a whole settle-up at once.
people:writeAdd people to a group and rename, archive or restore them.
members:writeChange a member's role in a group, and remove a member.
invitations:writeInvite people to a group by e-mail, revoke an invitation, and decide requests to join or to claim a person.
attachments:readList the files attached to an expense and get a short-lived link to download one.
attachments:writeAttach a file to an expense — sent as data, from a web address, or handed over by ChatGPT — and delete an attachment.
ledger:exportExport a group's ledger, as the portable backup file the app writes or as a spreadsheet.
The tools
71 tools, one operation each. A tool that reads is never also a tool that writes, every write takes a key of your agent's own so a retry cannot book the same expense twice, and every amount is a decimal string with its currency rather than a number.
This list is generated from the server itself, so it is what the endpoint publishes today.
Names and descriptions are English everywhere, because they are what the server publishes to every client rather than page copy.
Every tool also publishes the shape of its answer, generated from the same validator the server checks its own results against, so an assistant can plan against it without calling first.
activity_listReadsThe tool's description, as the server sends it
Lists a group's chat messages and recorded events, newest first, one page at a time. Answers "what happened in the group", "what changed since yesterday". Message text is returned as data, in the `text` field of each item; a long message is shortened here and `text_truncated` says so. Each entry carries a `subject`: the kind of record it is about and that record's id, so an expense or a payment mentioned here can be read or changed without looking it up. The subject of an `EXPENSE_DELETED` entry is a deleted expense, which `expenses_get_deleted` reads with the `version` a restore needs; a deleted payment still reads through `payments_get`. `since` answers "what changed lately" in one call instead of paging back through the timeline. Every entry also lists its reactions (`reactions`: each emoji, how many chose it and whether the signed-in person did).
- Permissions
groups:read chat:read- Required arguments
group_id- Result fields
group_id string, items object[], group_url string | null, next_cursor string | null, has_more boolean
attachments_add_from_dataChangesThe tool's description, as the server sends it
Attaches a photo or a PDF to an expense from the file's own bytes, base64 encoded. Use it for the receipt an expense came from, so everyone in the group can see what was spent -- but only with bytes you hold exactly: a file your host handed you as data, or a file on your own disk that your code read (a coding agent). A file the person shared in the chat goes through `attachments_add_from_file` where the client passes files to tools (ChatGPT). Anywhere else a chat file is out of reach: do not read it in a code sandbox, compress, re-encode or retype it -- that base64 would still have to be written out by you, which takes minutes and arrives damaged -- and give the person the expense's link (`url` on the expense in any answer) at once, to add it in the SplitPop app. SplitPop reads the whole file and refuses one that arrives incomplete or damaged. If you can run code, compute the file's SHA-256 there and send it as `sha256`, so SplitPop can confirm that every byte arrived. Do not attach documents containing full card numbers or identity documents. The bytes must really be the type given as `content_type`, and at most 5 MB decoded. Someone who may add expenses to the group may attach files to them.
- Permissions
groups:read attachments:write- Required arguments
group_id, expense_id, content_type, data, request_key- Result fields
attachment object, expense_id string, attachment_count number, expense_url string | null, group_id string, group_url string | null, is_replay boolean
attachments_add_from_fileChangesThe tool's description, as the server sends it
Attaches a photo or a PDF the person attached to the conversation to an expense, when the client passes the file itself as a file object in `file` (ChatGPT does: `download_url` and `file_id`), and SplitPop downloads it from the client's file host. Answers "put this receipt on the dinner expense". Use it for the receipt an expense came from, so everyone in the group can see it. A path is not a file object: a sandbox or local path (`/mnt/user-data/uploads/...`, `/tmp/...`, `C:\...`) never reaches SplitPop, so with only a path -- Claude's uploads are paths -- or no file object at all, do not call this tool; give the person the expense's link (`url` on the expense in any answer) at once, to add the file in the SplitPop app. JPEG, PNG, WebP or PDF, at most 5 MB, and it has to really be what it claims and arrive whole: SplitPop reads the file's own signature and its end. Do not attach documents containing full card numbers or identity documents. Someone who may add expenses to the group may attach files to them.
- Permissions
groups:read attachments:write- Required arguments
group_id, expense_id, request_key- Result fields
attachment object, expense_id string, attachment_count number, expense_url string | null, group_id string, group_url string | null, is_replay boolean
attachments_add_from_urlChangesThe tool's description, as the server sends it
Attaches a photo or a PDF to an expense by downloading it from an https address, for hosts that hand a model a link rather than the file itself. The address must be public and answer with the file itself: a plain http address, an IP address, a private host and a redirect are all refused, and so is a link to a page that needs a sign-in, as is anything larger than 5 MB, not a photo or a PDF, or cut short on the way. If you already hold the bytes, use `attachments_add_from_data` instead; if you have neither, give the person the expense's `url` to add the file in the SplitPop app.
- Permissions
groups:read attachments:write- Required arguments
group_id, expense_id, url, request_key- Result fields
attachment object, expense_id string, attachment_count number, expense_url string | null, group_id string, group_url string | null, is_replay boolean
attachments_deleteOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Removes a file from the expense it is attached to and deletes it. You may remove what you attached; an owner or an administrator may remove anything. The expense itself is untouched and no balance moves. This cannot be undone: the file would have to be attached again. The person must have asked for this file to go.
- Permissions
groups:read attachments:write- Required arguments
group_id, asset_id, request_key- Result fields
asset_id string, is_deleted true, expense_id string | null, attachment_count number, group_id string, group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
attachments_listReadsThe tool's description, as the server sends it
Lists the receipts and photographs attached to an expense: the file's name, type and size, and a link to download it. Anyone holding a link can download the file until it expires, a few minutes later. The tool returns links, not file contents. A link is left out when this deployment cannot sign one, and when you are reading a single expense that a group you are not in has published.
- Permissions
groups:read attachments:read- Required arguments
group_id, expense_id- Result fields
group_id string, expense_id string, attachments object[], can_download boolean, expense_url string | null
balances_listReadsThe tool's description, as the server sends it
Lists every person in a group with their net position in the group's currency: positive means the group owes them, negative means they owe the group. Answers "where does everybody stand" as one net figure per person; who pays whom to settle it is `settle_up_plan`. Where the app shows cards this read shows the balances as one, so call it when the person asks to see the balances; to check a group after a change, `groups_summary` answers without a card. `recent_changes` lists the expense and payment changes of the last 15 minutes, newest first (ids, amounts, whether the caller made them, and `via`: "card" when the writing client reported a SplitPop card button through its request key, which SplitPop does not verify), so "what changed?" is answered from it.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, currency string, people object[], group_url string | null, recent_changes object[]
balances_overviewReadsThe tool's description, as the server sends it
Answers "who owes me overall", "what do I owe across all my groups". Sums the caller's own net position across their most recent groups and returns one total per currency, plus the per-group figures behind it. Positive means those groups owe them, negative means they owe. Totals are per currency and never combined: no exchange rate is applied. It reads at most the 25 most recently joined groups; when the caller has more, `is_complete` is false and the totals cover only those 25. A group whose balance SplitPop will not state yet -- no seat, or a projection still reconciling -- comes back with a null balance and is left out of the totals. `by_person` answers "who owes me, and whom do I owe?" person by person: one row per person and currency, with what they owe the caller (`owes_me`), what the caller owes them (`i_owe`) and the groups behind it, added up from the transfers each group's settlement plan recommends -- the ones the app's Balances page shows. In a group whose transfer mode is SIMPLIFIED the plan settles everybody in the fewest payments, so somebody may be asked to pay the caller for a debt that was really somebody else's. `by_person_is_complete` is false when a counted group's balance or plan is not worked out yet -- a different thing from the 25-group window (`is_complete`) -- and `caveats` gives each that holds with its own reason (`GROUP_LIMIT`; `BALANCES_NOT_READY`: left out of the totals and the rows; `PLANS_NOT_READ`: in the totals, missing from the rows), to report each for what it is.
- Permissions
groups:read- Result fields
by_currency object[], by_group object[], group_count number, is_complete boolean, by_person object[], by_person_is_complete boolean, caveats object[]
categories_suggestReadsThe tool's description, as the server sends it
Says which category SplitPop would give an expense with this title, and how sure it is, and records nothing. It is exactly what `expenses_create` stores when `category` is left out. `confidence` is "HIGH" when one category clearly won; "LOW" (something matched, but nothing clearly) and "NONE" (nothing known in the title) both answer GENERAL -- ask the person when that matters. `matched_terms` are the words that decided it. Each call runs SplitPop's whole lexicon, so it (together with `expenses_preview`) is limited to 30 calls a minute per person; do not use it to sort long lists. Categories: `GENERAL` (General: anything unclear or mixed, and the default); `EATING_OUT` (Eating out: restaurants, cafés, coffee, takeaway, delivery, snacks bought out); `GROCERIES` (Groceries: supermarkets, markets, bakeries, food shopping); `DRINKS_NIGHTLIFE` (Drinks & nightlife: bars, pubs, clubs, beer, wine, rounds, apéro); `ACCOMMODATION` (Accommodation: hotels, hostels, rentals for a stay, camping, tourist tax); `FLIGHTS` (Flights: flights, airlines, baggage fees); `TRANSPORT` (Transport: taxis, ride-hailing, trains, buses, ferries, public transport, transfers); `CAR_FUEL` (Car & fuel: fuel, charging, parking, tolls, car rental, repairs); `ACTIVITIES` (Activities: tickets, tours, museums, cinema, concerts, sports, ski passes, boat trips); `SHOPPING` (Shopping: clothes, electronics, souvenirs, books, general retail); `GIFTS` (Gifts: presents, flowers, gift cards, donations); `HOME_RENT` (Home & rent: rent, furniture, household supplies, cleaning, repairs at home); `UTILITIES_SUBSCRIPTIONS` (Utilities & subscriptions: electricity, heating, water, internet, phone plans, streaming); `HEALTH_WELLNESS` (Health & wellness: pharmacy, doctor, dentist, spa, massage, gym, hairdresser); `FEES_SERVICES` (Fees & services: bank and card fees, visas, insurance, tips, laundry, postage).
- Permissions
groups:read- Required arguments
title- Result fields
category "GENERAL" | "EATING_OUT" | "GROCERIES" | "DRINKS_NIGHTLIFE" | "ACCOMMODATION" | "FLIGHTS" | "TRANSPORT" | "CAR_FUEL" | "ACTIVITIES" | "SHOPPING" | "GIFTS" | "HOME_RENT" | "UTILITIES_SUBSCRIPTIONS" | "HEALTH_WELLNESS" | "FEES_SERVICES", emoji string, confidence "HIGH" | "LOW" | "NONE", matched_terms string[]
chat_deleteOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Removes a message from a group's activity. The entry stays in the timeline as a deleted message, because replies point at it, and its text is gone. You may delete your own message for four hours after you posted it; an owner or an administrator may delete anybody's, their own included, with no time limit, which the group sees as moderated. The ledger's own entries are not text and cannot be deleted. Use it only for a message the person asked to remove.
- Permissions
groups:read chat:write- Required arguments
group_id, message_id, request_key- Result fields
message_id string, is_deleted true, group_id string, group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
chat_editOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Replaces the text of a message in a group's activity. You may edit your own message for four hours after you posted it; an owner or an administrator may edit anybody's, their own included, with no time limit, which the group sees as a moderated edit. The old text is not kept. The ledger's own entries are not text and cannot be edited. Use it only for a change the person asked for.
- Permissions
groups:read chat:write- Required arguments
group_id, message_id, text, request_key- Result fields
message_id string, edited_at string, group_id string, group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
chat_postChangesThe tool's description, as the server sends it
Posts the text exactly as given in a group's activity, under the signed-in person's name; use it only for a message the person asked to post. It appears in the group's timeline the next time somebody opens it. Posting sends no notification, by e-mail or otherwise. The author may change or remove it afterwards with `chat_edit` and `chat_delete`, within the four-hour window that applies to their own messages.
- Permissions
groups:read chat:write- Required arguments
group_id, text, request_key- Result fields
message_id string, created_at string, reply_to_id string | null, group_id string, group_url string | null, is_replay boolean
chat_reactChangesThe tool's description, as the server sends it
Puts one of SplitPop's reactions on a message, or takes yours off again if it is already there. It is a toggle: `activity_list` shows each message's reactions with `is_mine`, so you know beforehand which of the two it will do, and `is_on` in the answer says which just happened. Anybody who can write in the group may react to any message.
- Permissions
groups:read chat:write- Required arguments
group_id, message_id, emoji, request_key- Result fields
message_id string, emoji string, is_on boolean, group_id string, group_url string | null, is_replay boolean
claims_decideOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Recognises somebody as a person the group already has a name for, or declines them, and tells them either way. Approving hands them that person's whole history and balance, so check with the person whose group it is that the claim is genuine before calling this. Only a group administrator or its owner may do this.
- Permissions
groups:read invitations:write- Required arguments
group_id, request_id, decision, request_key- Result fields
request_id string, request_type "JOIN_NEW" | "CLAIM_EXISTING", decision "APPROVED" | "REJECTED", group_id string, requests object[], group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
claims_listReadsThe tool's description, as the server sends it
Lists the people waiting to be recognised as somebody the group already has a name for, with the person each one is claiming. Only a group administrator or its owner may read this. A request to join as somebody new is a different thing: `join_requests_list` returns those.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, requests object[], group_url string | null
contacts_getReadsThe tool's description, as the server sends it
Reads one contact, as the app's contact page shows them: their name, e-mail address, whether they have a SplitPop account, and every group the signed-in person shares with them. Somebody who shares no group with the signed-in person reads as not found.
- Permissions
groups:read- Required arguments
person_id- Result fields
person_id string, display_name string, email string | null, has_account boolean, shared_groups object[], url string | null
contacts_listReadsThe tool's description, as the server sends it
Lists the signed-in person's contacts, as the app's contacts page does. Answers "who do I share groups with", "find Mia". Everybody they share a group with, once each, under their own name, alphabetically, with the groups they share and whether they have a SplitPop account. A contact's `person_id` is what `people_add` takes in `contact_person_ids` to add them to another group. `query` narrows the list to names or e-mail addresses containing some text. The e-mail address is shown as the app shows it on that page, to anybody who shares a group with that person.
- Permissions
groups:read- Result fields
contacts object[], next_cursor string | null, has_more boolean
currency_rateReadsThe tool's description, as the server sends it
Reads the exchange rate SplitPop would use today between a group's base currency and another currency, with the provider and the day it published that rate. SplitPop fetches the pair from its rate provider if it has none cached, which a member of the group may do and a guest of a public one may not. One of the two currencies must be the group's base currency. `expenses_create`, `expenses_create_many` and `expenses_update` fetch the rate they need themselves, so this is not needed before them; `expenses_preview` does not fetch one.
- Permissions
groups:read- Required arguments
group_id, base_currency, target_currency- Result fields
state "UNAVAILABLE" | "AVAILABLE", group_id string, base_currency? string, target_currency? string, rate? string, provider? string, provider_as_of? string, fetched_at? string
expenses_createOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Adds an expense to a group and updates everybody's balances. Answers "add an expense", "I paid 60 for dinner, split it with Mia and Jonas", "split the bill". The person must have confirmed the amount, the currency, who paid and how it is split before this runs. When the split is not equal, or amounts are large or in another currency, call `expenses_preview` first and show the person SplitPop's own shares. State an expense in the currency it was paid in: SplitPop converts it into the group's `base_currency` at a rate it fetches itself and keeps both amounts -- never send a converted amount or a rate. For an amount in another currency SplitPop may fetch that rate from its rate provider before writing. `payers` and `split` name each person by `participant_id`, exact name, or "@me" for the signed-in person; `split.participants: "everyone"` (with `except` to leave some out) divides it between the whole group without a `people_list` call. In a fixed split the people listed without a `value` share the rest equally ("Ben had a drink for 8, the rest of us split it"), and `mode: "items"` divides a bill by who had what, with a tip, tax or discount spread over it; the answer's `breakdown` says who had what. "Same as last month" is `expenses_repeat`. The group's other members are notified, as their notification settings allow. Returns the expense, the caller's new net position, everybody's balances and the group's link; `possible_duplicate_of` names a very similar expense already in the group, and `undo` is the `expenses_delete` call that takes this one back.
- Permissions
groups:read expenses:write- Required arguments
group_id, title, amount, payers, split, request_key- Result fields
expense object, breakdown object[] | null, possible_duplicate_of object | null, undo object, group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
expenses_create_manyOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Adds several expenses to one group in a single transaction: either every one is recorded or none is. Use it for several expenses at once -- "I paid these: groceries 84, the taxi 42, the hotel 380" -- or a receipt the person wants as separate rows; each line becomes an expense of its own, with its own payers, split, date and category. A bill divided by who had what is one expense: `expenses_create` with `mode: "items"`, one row and one notification. `expenses_create` is for a single expense. The person must have confirmed the list once, line by line, before this runs; one confirmation of the whole list is enough. The group's other members are notified about each line, as their notification settings allow, exactly as if you had sent them one at a time, so keep a batch to what a person would really enter. At most 20 lines in one call, and fewer in a group with many members, because every line notifies all of them; the refusal says how many that group allows. A line sent without `category` is filed from its title, and the answer carries each line's category. `expenses_preview_many` checks the lines first. State an expense in the currency it was paid in: SplitPop converts it into the group's `base_currency` at a rate it fetches itself and keeps both amounts -- never send a converted amount or a rate. The answer's `undo` lists one `expenses_delete` call per line.
- Permissions
groups:read expenses:write- Required arguments
group_id, expenses, request_key- Result fields
expenses object[], possible_duplicate_of object[], skipped_lines object[], breakdowns object[], undo object[], group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
expenses_deleteOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Deletes an expense and reverses its effect on everybody's balances. Answers "delete that", "remove the duplicate", "undo the taxi". Before calling, say exactly which expense -- its title, amount and who paid -- and ask once, even when the person asked for it by name; their yes covers it (send `confirmed_with_person: true`). The one exception is an expense the person already confirmed in the previous turn, after your read-back or on a SplitPop card. When their words fit more than one (two taxis), name each with its amount and date in that question. When `groups_get` says your `my_role` is MEMBER, say in the same question that it cannot be brought back from here. Read it with `expenses_get` first and send its `version` as `expected_version`. Afterwards tell the person exactly what was removed -- the answer's `deleted` holds its title, amount and who paid -- and how it comes back: only a group owner or administrator can bring it back, with `expenses_restore` and the `version` this call answers with (the answer's `undo` is that exact call, and `undo.is_allowed` says whether the caller may make it) or the one `expenses_get_deleted` reads later, or from the expense's page in the SplitPop app.
- Permissions
groups:read expenses:write- Required arguments
group_id, expense_id, expected_version, request_key- Result fields
expense_id string, is_deleted true, version number, deleted object, undo object, group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
expenses_getReadsThe tool's description, as the server sends it
Reads one expense in full: who paid, how it is split and on what basis, the amount as entered and as converted, the exchange rate behind that conversion, and how many files are attached. For anybody who may read the group: its members, and anybody signed in reading a public group as a guest, who sees what the app shows a guest. For a single expense shared by link out of a group you cannot read, use `public_expense_get`. A deleted expense answers with a sentence pointing at `expenses_get_deleted`.
- Permissions
groups:read- Required arguments
group_id, expense_id- Result fields
expense_id string, group_id string, title string, notes string | null, occurred_at string, split_mode "EQUAL" | "SHARES" | "PERCENT" | "FIXED", amount object, entered_amount object, fx object | null, payers object[], shares object[], category "GENERAL" | "EATING_OUT" | "GROCERIES" | "DRINKS_NIGHTLIFE" | "ACCOMMODATION" | "FLIGHTS" | "TRANSPORT" | "CAR_FUEL" | "ACTIVITIES" | "SHOPPING" | "GIFTS" | "HOME_RENT" | "UTILITIES_SUBSCRIPTIONS" | "HEALTH_WELLNESS" | "FEES_SERVICES", category_source "AUTO" | "MANUAL", attachment_count number, is_public boolean, public_url string | null, url string | null, version number, created_at string | null, updated_at string | null
expenses_get_deletedReadsThe tool's description, as the server sends it
Reads an expense that has been deleted, as the page the app keeps for it. Answers "what was that expense I deleted", "can we bring it back". Its title, date, amount and category, when it was deleted, and the `version` that `expenses_restore` takes as `expected_version`. `can_restore` says whether the signed-in person could bring it back now -- only a group's owner or an administrator can, and never while `restore_block` names a reason (`LINES_UNAVAILABLE`: deleted before SplitPop kept its split; `CURRENCY_CHANGED`: the group's currency has changed since, and `amount` is null). An expense that has not been deleted reads through `expenses_get` instead. The ids of deleted expenses are on `activity_list` entries of type `EXPENSE_DELETED`.
- Permissions
groups:read- Required arguments
group_id, expense_id- Result fields
expense_id string, group_id string, title string, occurred_at string, deleted_at string, category "GENERAL" | "EATING_OUT" | "GROCERIES" | "DRINKS_NIGHTLIFE" | "ACCOMMODATION" | "FLIGHTS" | "TRANSPORT" | "CAR_FUEL" | "ACTIVITIES" | "SHOPPING" | "GIFTS" | "HOME_RENT" | "UTILITIES_SUBSCRIPTIONS" | "HEALTH_WELLNESS" | "FEES_SERVICES", amount object | null, version number, restore_block one of 3 shapes, can_restore boolean, url string | null
expenses_previewReadsThe tool's description, as the server sends it
Works out exactly what an expense would do, and records nothing. Answers "how much would each of us pay", "check this split before adding it": each person's share as SplitPop would compute it, including how the rounding falls, and what everybody's net position would be afterwards. Use it before `expenses_create`: it takes the same `amount`, `payers` and `split` -- the "everyone" and itemized helpers included, with who had what in `breakdown` -- and refuses whatever the write would refuse, so a preview that comes back is a write that will be accepted. For an amount in another currency it fetches the rate itself when SplitPop holds none, exactly as the write does, and shows the rate and the converted amount (`fx`, `amount` beside `entered_amount`). With `title` it also says whether the group already holds a very similar expense (`possible_duplicate_of`, the write's own check), and which category the expense would be filed under (`category`, `category_source` and, for SplitPop's own choice, `category_confidence`). It needs the same permission to write that the write does. Its `checked_at` goes back with the create the person confirms, so an expense recorded in between is caught instead of booked twice. Several lines of one receipt are previewed together with `expenses_preview_many`.
- Permissions
groups:read expenses:write- Required arguments
group_id, amount, payers, split- Result fields
group_id string, currency string, amount object, entered_amount object, fx object | null, split_mode "EQUAL" | "SHARES" | "PERCENT" | "FIXED", payers object[], shares object[], breakdown object[] | null, possible_duplicate_of object | null, identical_lines number, balances_ready boolean, balances object[], group_url string | null, checked_at string, category "GENERAL" | "EATING_OUT" | "GROCERIES" | "DRINKS_NIGHTLIFE" | "ACCOMMODATION" | "FLIGHTS" | "TRANSPORT" | "CAR_FUEL" | "ACTIVITIES" | "SHOPPING" | "GIFTS" | "HOME_RENT" | "UTILITIES_SUBSCRIPTIONS" | "HEALTH_WELLNESS" | "FEES_SERVICES" | null, category_source one of 3 shapes, category_confidence "HIGH" | "LOW" | "NONE" | null, writes_blocked object | null
expenses_preview_manyReadsThe tool's description, as the server sends it
Works out exactly what `expenses_create_many` would write -- several expenses at once, each its own row -- and records nothing: each line's amount, shares and who had what (`breakdown` for an itemized split), the category it would be filed under (`category`, `category_source`, `category_confidence`, per line), whether the group already holds a very similar expense (`possible_duplicate_of`, per line), the lines' total, and where everybody would stand after all of them. Answers "check these before you add them". It takes the same lines as `expenses_create_many` and refuses whatever that write would refuse -- naming the line -- so a preview that comes back is a batch that will be accepted. An amount in another currency is priced as the write prices it: SplitPop fetches the rate itself when it holds none. One bill divided by who had what is one expense, previewed with `expenses_preview`. At most 20 lines, fewer in a group with many members, exactly as the write. Its `checked_at` goes back with the `expenses_create_many` the person confirms, so a line recorded in between is caught instead of booked twice.
- Permissions
groups:read expenses:write- Required arguments
group_id, expenses- Result fields
group_id string, currency string, lines object[], total object, balances_ready boolean, balances object[], group_url string | null, checked_at string, writes_blocked object | null
expenses_repeatOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Adds a new expense stated like an earlier one: its title, its amount and currency, who paid and how it was divided, and its category if the person had picked it -- with anything you send overriding that. Answers "same as last month", "add October's rent like September's", "the usual groceries split". The new expense is dated now unless `date` says otherwise, is priced at today's rate if it is in another currency, keeps no note unless you send one, and lets SplitPop choose its category from the title unless the earlier one was picked by a person. A new `amount` keeps a single payer and an equal, shares or percent split as they were; with several payers send `payers` too, and for a split by fixed amounts send `split` (leave out the `value` of the people who share the rest). Find the earlier expense with `ledger_list` (`query` or `category`). The person must have confirmed what is being added -- repeat it back with the amount and the date -- and the group's other members are notified, as for `expenses_create`. For a schedule, use a `request_key` made of the period ("rent-2026-10") so a task that fires twice books it once. Returns what `expenses_create` returns, with `repeated_from_expense_id`; `undo` is the `expenses_delete` call that takes it back.
- Permissions
groups:read expenses:write- Required arguments
group_id, expense_id, request_key- Result fields
expense object, breakdown object[] | null, possible_duplicate_of object | null, undo object, group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated", repeated_from_expense_id string
expenses_restoreChangesThe tool's description, as the server sends it
Brings a deleted expense back, exactly as it was, and restores its effect on everybody's balances. Only a group administrator or its owner may do this; anyone else is refused. `expected_version` is the `version` `expenses_delete` answered with, or the one `expenses_get_deleted` reads for any deleted expense -- together with whether a restore would be refused. Files that were attached to it are not restored: deleting the expense released them. The answer's `undo` is the `expenses_delete` call that deletes it again.
- Permissions
groups:read expenses:write- Required arguments
group_id, expense_id, expected_version, request_key- Result fields
expense object, undo object, group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
expenses_set_categoryOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Changes only what an expense was filed under, and nothing else: no amounts or balances change, nobody is notified and no activity event is written. Send `category` for the one the person picked -- it is then kept whatever the title becomes -- or `automatic: true` to let SplitPop choose from the title again and keep following it. Read the expense with `expenses_get` first and send its `version` as `expected_version`; the answer carries the new one. Asking for the category it already has changes nothing. The person should have asked for this, or agreed to it. The category it replaces is in the answer's `undo`, the call that puts it back. Categories: `GENERAL` (General), `EATING_OUT` (Eating out), `GROCERIES` (Groceries), `DRINKS_NIGHTLIFE` (Drinks & nightlife), `ACCOMMODATION` (Accommodation), `FLIGHTS` (Flights), `TRANSPORT` (Transport), `CAR_FUEL` (Car & fuel), `ACTIVITIES` (Activities), `SHOPPING` (Shopping), `GIFTS` (Gifts), `HOME_RENT` (Home & rent), `UTILITIES_SUBSCRIPTIONS` (Utilities & subscriptions), `HEALTH_WELLNESS` (Health & wellness), `FEES_SERVICES` (Fees & services); `categories_suggest` says what belongs where.
- Permissions
groups:read expenses:write- Required arguments
group_id, expense_id, expected_version, request_key- Result fields
expense object, undo object, group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
expenses_updateOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Changes an expense and recomputes everybody's balances. Read it with `expenses_get` first and send its `version` as `expected_version`; if anyone changed it in between, the write is refused and you read it again. What an expense cost, who paid it and how it is divided are one statement: send `amount`, `payers` and `split` together or leave all three out. `split` takes the same helpers as `expenses_create` -- "everyone" with `except`, people without a `value` sharing the rest of a fixed split, and `mode: "items"`, whose `breakdown` the answer carries. What it replaces is not kept in SplitPop, but the answer's `undo` is the `expenses_update` call that puts the previous title, amount, date, note, payers, split and category back -- to the cent while the currency stays the same; after an edit that moved it off a foreign currency the undo prices the old amount at today's rate, and says so with `is_exact: false`. An expense keeps the exchange rate it was recorded with while its currency stays the same, so changing a note or a title never re-prices it; sending a different currency, or the group's own, re-prices it at today's rate, which SplitPop may fetch from its rate provider. The person must have confirmed the change. The group's other members are notified, as their notification settings allow -- except for a change of `category` alone, which is quiet, exactly like `expenses_set_category`. An automatic category follows a new `title`; one the person picked stays.
- Permissions
groups:read expenses:write- Required arguments
group_id, expense_id, expected_version, request_key- Result fields
expense object, breakdown object[] | null, undo one of 2 shapes, group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
fetchReadsThe tool's description, as the server sends it
Reads one document that `search` returned, by its id, as text with its web address for citation. A SplitPop group or expense link is accepted in place of the id. A group id reads the group's totals, the caller's position and the transfers that would settle it; an expense id reads the expense with its payers, its split and its category.
- Permissions
groups:read- Required arguments
id- Result fields
id string, title string, text string, url string, metadata { [key]: string }
groups_clone_peopleChangesThe tool's description, as the server sends it
Creates a group and adds everybody who is in another group the caller is a member of, in one transaction. Only the people come across: no expenses, no payments, no balances and none of the other group's settings, so the new group starts empty with the same table around it. Archived people and the caller are left out — the caller is the new group's owner and first participant already. At most 10 people come across in one call: a larger group is refused before anything is created, and is set up with `groups_create` and then `people_add` in batches of 10. The caller has to be a member of the group being copied. Confirm the new group's name and its ledger currency with the person first; the currency cannot be changed once anything is recorded. The group is public, which is the point of its link: it opens the whole group for anyone who has it, with no account. Leave `visibility` out; send `"PRIVATE"` only when the person explicitly says private, never on your own to be careful. Tell the person it is public and that the settings or `groups_update` make it private. Anybody joining beside the copied people goes in the same call: Name the people in this same call, by plain names in `participant_names` -- no `contacts_list` lookup and no question of your own first -- never create first and add them afterwards: a group cannot be deleted. If a name may be somebody they know, nothing is created and the answer is the one question to ask ("Mia from Ski trip and Jonas from Flat share — same people or new?"); after they answered -- never before -- send the call again with that refusal's `same_or_new_token` and `same_person_confirmed: true` or `create_new_people: true` as they answered.
- Permissions
groups:read groups:write people:write- Required arguments
from_group_id, name, currency, request_key- Result fields
group object, people object[], copied_from_group_id string, group_url string | null, matching_contacts object[], linked_people object[], is_replay boolean, tell_the_person string
groups_createChangesThe tool's description, as the server sends it
Creates a group with the signed-in person as its owner and first participant, and the people it is for, and returns it with the link to share. With a first expense already in hand, `groups_create_with_expense` does all of it in one call. Confirm the ledger currency with the person before calling: it is fixed at creation and cannot be changed once anything is recorded. The group is public, which is the point of its link: anyone who has it reads the whole group, with no account. Leave `visibility` out; send `"PRIVATE"` only when the person explicitly says private, never on your own to be careful. Tell the person it is public and that the group's settings or `groups_update` make it private. Name the people in this same call, by plain names in `participant_names` -- no `contacts_list` lookup and no question of your own first -- never create first and add them afterwards: a group cannot be deleted. If a name may be somebody they know, nothing is created and the answer is the one question to ask ("Mia from Ski trip and Jonas from Flat share — same people or new?"); after they answered -- never before -- send the call again with that refusal's `same_or_new_token` and `same_person_confirmed: true` or `create_new_people: true` as they answered.
- Permissions
groups:read groups:write people:write- Required arguments
name, currency, request_key- Result fields
group object, people object[], group_url string | null, matching_contacts object[], linked_people object[], is_replay boolean, tell_the_person string
groups_create_with_expenseChangesThe tool's description, as the server sends it
Creates a group, adds the people named in it and records its first expense, all at once, and answers with the link to share. Answers "split this with Mia and Jonas" when they are not in a SplitPop group yet; for a group that already exists, call `groups_list`, then `expenses_create`. Confirm four things with the person before calling: the amount, the currency, who paid and how it is split. Name the people in this same call, by plain names in `participant_names` -- no `contacts_list` lookup and no question of your own first -- never create first and add them afterwards: a group cannot be deleted. If a name may be somebody they know, nothing is created and the answer is the one question to ask ("Mia from Ski trip and Jonas from Flat share — same people or new?"); after they answered -- never before -- send the call again with that refusal's `same_or_new_token` and `same_person_confirmed: true` or `create_new_people: true` as they answered. Nobody is linked or duplicated on a guess. By default the signed-in person paid and the expense is divided equally between them and everyone added. The group is public, the point of its link: it opens the whole group, expense included, for anyone who has it. Send `"PRIVATE"` only when the person explicitly says private, never to be careful. Tell them it is public and that the settings make it private. The ledger currency is fixed at creation, and this first expense is stated in it; an amount paid in another currency is added afterwards with `expenses_create`, which converts it. At most 10 names in one call: for a bigger table, create the group with `groups_create`, add the people with `people_add` in batches and then the expense. The group's members are notified of the expense, as their settings allow. Without `category` SplitPop files the expense from its title, and the answer carries that category. Nothing is written unless all of it can be. SplitPop has no way to delete a group, so the answer's `undo` deletes only the expense.
- Permissions
groups:read groups:write expenses:write people:write- Required arguments
name, currency, title, amount, request_key- Result fields
share_link string | null, group object, expense object, breakdown object[] | null, undo object, matching_contacts object[], linked_people object[], group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
groups_getReadsThe tool's description, as the server sends it
Reads one SplitPop group: its name, currencies, visibility, the caller's role and participant, what the caller is allowed to change in it, and the `settings_version` that `groups_update` needs. A signed-in person who is not a member of a public group reads it as `my_role: "GUEST"` with every ability false: the reads work for them, the writes do not.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, name string, base_currency string, display_currency string | null, visibility "PUBLIC" | "PRIVATE", my_role "OWNER" | "ADMIN" | "MEMBER" | "GUEST", my_participant_id string | null, recommended_transfer_mode "SIMPLIFIED" | "DIRECT", join_policy object, has_financial_activity boolean, settings_version number, abilities object, url string | null, writes_blocked object | null
groups_join_optionsReadsThe tool's description, as the server sends it
Says how the signed-in person could get into a group they are not in yet, as the join card the app shows on the group's link: whether it accepts a request to join as somebody new (`can_join_as_new`, with the `suggested_name` it would be sent under), whether it accepts a claim on one of the people it already has (`can_request_claim`), and which people nobody's account has claimed yet (`claimable_people`, the ones a claim may name). Answers "can I join this group", "I'm Benny in that list, link me". Also the person's own latest request and where it stands. `state: "MEMBER"` means they are in already. The `group_id` comes from the link the person was given -- the whole link is accepted -- and works for a private group too, whose link shows only this. Ask with `join_requests_create`.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, group_name string, state "MEMBER" | "NON_MEMBER", can_request_claim boolean, can_join_as_new boolean, suggested_name string | null, claimable_people object[], latest_request object | null, group_url string | null
groups_listReadsThe tool's description, as the server sends it
Lists the SplitPop groups the signed-in person belongs to, newest first, with their role in each, their own `my_participant_id` and their net position in the group's currency. Answers "which groups am I in", "what is my balance in each". A public group the person only opened by its link is not listed: the id in that link (`/group/<group_id>`) works directly with the read tools.
- Permissions
groups:read- Result fields
groups object[], writes_blocked object | null
groups_share_linkReadsThe tool's description, as the server sends it
Returns the web address of a group, for sharing with someone. A public group reads in full at that address; a private group shows only its name and a request-to-join card.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, name string, visibility "PUBLIC" | "PRIVATE", url string | null
groups_summaryReadsThe tool's description, as the server sends it
Reads what a group adds up to: how many people, expenses and payments it holds, what has been spent, the caller's own net position, and the transfers SplitPop recommends to settle it. Answers "how much have we spent", "how many expenses are in it", "what do I owe" in one call. For "who owes whom" or "settle up", `settle_up_plan` returns the same plan and shows it to the person as a card where the app can. In the SIMPLIFIED transfer mode the plan keeps the number of payments low, so somebody may be asked to pay a different person than the one they owe directly; `recommended_transfer_mode` says which mode the group uses. `spending_by_category` is where the money went, as the app's Summary shows it: the five biggest categories of all time, each with its share of everything spent (`stats_by_category` has every category, for any period); null while SplitPop is still preparing the group's totals. It shows no card, so it is the read for "what changed?" and for checking a group after the person used a card. `recent_changes` lists the expense and payment changes of the last 15 minutes, newest first (ids, amounts, whether the caller made them, and `via`: "card" when the writing client reported a SplitPop card button through its request key, which SplitPop does not verify), so "what changed?" is answered from it.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, name string, currency string, counts object, total_spent object | null, my_position object | null, recommended_transfer_mode "SIMPLIFIED" | "DIRECT", is_settlement_plan_ready boolean, recommended_transfers object[], url string | null, spending_by_category object[] | null, recent_changes object[], writes_blocked object | null
groups_updateOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Changes what a group is called, the emoji beside it, the second currency amounts are shown in, whether its link opens the group to anyone who has it, what a stranger holding that link may ask for, and how the settle-up plan is worked out (`transfer_mode`). Answers "show direct debts instead of the simplified plan". The ledger currency is not changed here and cannot be changed once anything has been recorded. Read the group with `groups_get` first and send its `settings_version` as `expected_version`. Only a group administrator or its owner may do this. Publishing a group, or making it private again, changes who can read every expense, payment and balance in it: confirm it with the person before calling this.
- Permissions
groups:read groups:write- Required arguments
group_id, expected_version, request_key- Result fields
group object, group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
invitations_createOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Invites an e-mail address to a group. SplitPop e-mails that address the invitation itself, with a signed link that works only for them and expires after seven days; the link is never returned here, and an administrator who needs it copies it in the SplitPop app. SplitPop mails one address at most once a day per group -- inviting the same address again within a day keeps the invitation already sent, with the role you give now -- and does not mail an address that asked not to get invitations or has had several today; the invitation is created either way and its link works. If the address already belongs to a member, nothing is sent and the answer says so. Only a group administrator or its owner may do this. Confirm the address and the role with the person before calling this: the mail goes out immediately.
- Permissions
groups:read invitations:write- Required arguments
group_id, email, request_key- Result fields
status "CREATED" | "ALREADY_MEMBER", invitation_id string | null, expires_at string | null, group_id string, invitations object[], group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
invitations_listReadsThe tool's description, as the server sends it
Lists the e-mail invitations to a group that nobody has accepted yet, with the role each one grants and when it expires. Only a group administrator or its owner may read this.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, invitations object[], group_url string | null
invitations_revokeOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Cancels an invitation nobody has accepted yet, so its link stops working, and tells the address it was sent to. An invitation that has already been accepted cannot be revoked; remove the member with `members_remove` instead. Only a group administrator or its owner may do this. Confirm which invitation with the person first: the mail saying it was withdrawn goes out immediately.
- Permissions
groups:read invitations:write- Required arguments
group_id, invitation_id, request_key- Result fields
invitation_id string, is_revoked true, group_id string, invitations object[], group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
join_requests_createOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Asks to be let into a group the signed-in person is not in: as somebody new (`request_type: "JOIN_NEW"`, under `requested_name`, which defaults to their own name unless SplitPop has only their login address), or as one of the people the group already has (`"CLAIM_EXISTING"` with `participant_id`, one of `claimable_people` from `groups_join_options`). Answers "ask to join this group", "that Benny is me". Nothing is granted until an owner or an administrator approves it; they are notified, and the person is told their decision in the app. Claim only a person who really is the signed-in person: approving hands over that person's whole history and balance. The person must have confirmed which route and which name before this runs. A request already waiting in the group is answered with `is_existing_request: true` and nothing new is sent. Read `groups_join_options` first: it says which routes the group accepts.
- Permissions
groups:read groups:write- Required arguments
group_id, request_type, request_key- Result fields
request_id string, group_id string, request_type "CLAIM_EXISTING" | "JOIN_NEW", status "PENDING", participant_id string | null, requested_name string | null, is_existing_request boolean, group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
join_requests_decideOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Lets somebody into a group as a new person, or declines them, and tells them either way. Approving adds them both as a member and as a person the ledger can settle with. Only a group administrator or its owner may do this. Ask the person whose group it is before deciding.
- Permissions
groups:read invitations:write- Required arguments
group_id, request_id, decision, request_key- Result fields
request_id string, request_type "JOIN_NEW" | "CLAIM_EXISTING", decision "APPROVED" | "REJECTED", group_id string, requests object[], group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
join_requests_listReadsThe tool's description, as the server sends it
Lists the people waiting to be let into a group as somebody new, with the name each one asked to be called. Only a group administrator or its owner may read this. A claim on a person the group already knows is a different thing: `claims_list` returns those.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, requests object[], group_url string | null
join_requests_withdrawOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Takes back the signed-in person's own request to join a group (or to claim one of its people) while it still waits for an owner or an administrator. Answers "never mind, cancel my request to join". It is quiet: nobody is notified, and the person may ask again with `join_requests_create`. Only their own waiting request can be withdrawn: anyone else's, or an id that names nothing, answers as not found, and one already decided says so. `request_id` is `latest_request.request_id` from `groups_join_options`. The person must have confirmed first.
- Permissions
groups:read groups:write- Required arguments
group_id, request_id, request_key- Result fields
request_id string, group_id string, is_withdrawn true, group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
ledger_exportReadsThe tool's description, as the server sends it
Returns a group's complete ledger, in one of two shapes. Answers "export this for my accountant", "put it in a spreadsheet", "back up the group". `format: "csv"` is for a person to read: one row per expense and payment, newest first, with decimal amounts, people's names and ISO calendar dates in UTC, ready for a spreadsheet. `format: "json"` is the default and is the portable backup file SplitPop itself writes and can import again: every person, expense and payment with exact amounts, exchange-rate provenance and split inputs. In the JSON, every amount is an integer string of minor units with the currency's `exponent` beside it: {"amount": "6000", "exponent": 2} is 60.00. The CSV carries decimal amounts. Neither shape carries attachments, chat or membership. Only a group administrator or its owner may do this, and only for a group with at most 100 expenses and payments together whose document is at most 48 KiB, so that every client can show the whole of it; anything larger is refused, and is read a page at a time with `ledger_list` or exported from the SplitPop app. The CSV takes `ledger_list`'s filters -- `type`, `participant_id` with `role`, `category`, `from`/`to` and a title `query` -- and then holds exactly the entries `ledger_list` returns for them ("everything Mia paid in September" is her `participant_id`, `role: "PAYER"` and the month's dates); the JSON backup is always the whole ledger and takes none.
- Permissions
groups:read ledger:export- Required arguments
group_id- Result fields
group_id string, format "csv" | "json", snapshot object, columns? string[], row_count? number, csv? string, entry_count? number, backup? object
ledger_listReadsThe tool's description, as the server sends it
Lists a group's expenses and payments, newest first, one page at a time. Answers "show the expenses", "what did Mia pay", "find last week's groceries". Can be narrowed to one kind, one person, what that person did (`role`: what they paid, or what was for them -- "what did Mia pay?" is `participant_id` plus `role: "PAYER"`), one category (`category`), a date range, or expenses whose title contains some text (`query`). Every expense entry carries its `category` and whether SplitPop chose it (`category_source`), its `version` (the `expected_version` an edit, a delete or a category change takes), the amount as entered (`entered_amount`), the `split_mode`, its note and how many files are attached (`attachment_count`); a payment entry carries its `version` too. On a page filtered by `participant_id`, each expense also says what that person paid towards it (`participant_paid`) and their share of it (`participant_share`). `is_mine` marks an expense the signed-in person paid for and a payment they sent or received, without filtering. A note is shortened here when it is long, and `notes_truncated` or `note_truncated` says so; `expenses_get` and `payments_get` return it whole. A page whose entries name very many payers comes back shorter than `limit`, with `has_more` and `next_cursor` to continue. On the first page, `recent_changes` lists the expense and payment changes of the last 15 minutes, newest first (ids, amounts, whether the caller made them, and `via`: "card" when the writing client reported a SplitPop card button through its request key, which SplitPop does not verify), so "what changed?" is answered from it.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, items one of 2 shapes[], search object | null, group_url string | null, recent_changes object[], next_cursor string | null, has_more boolean
members_listReadsThe tool's description, as the server sends it
Lists the accounts that have access to a group, with each one's role, whether their account is still active, and the participant they settle as when they have claimed one. Only a group administrator or its owner may read this; everyone else reads `people_list` instead.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, members object[], group_url string | null
members_removeOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Takes a member's access to a group away. Everything they recorded stays exactly as it is, and so does the person the ledger settles with, because removing that would rewrite the balances. The owner cannot be removed. An administrator may remove anybody else; anybody may remove themselves, which is how a person leaves a group.
- Permissions
groups:read members:write- Required arguments
group_id, member_id, request_key- Result fields
group_id string, members object[], group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
members_set_roleOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Makes a member an administrator of a group, or makes an administrator an ordinary member again. An administrator may add and archive people, invite by e-mail, decide requests to join, and change the group's settings. The owner's own role cannot be changed by anybody. Only a group administrator or its owner may do this.
- Permissions
groups:read members:write- Required arguments
group_id, member_id, role, request_key- Result fields
group_id string, members object[], group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
payments_createOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Records that one person in a group paid another, which settles that much of what they owed. Answers "Anna paid me back", "I sent Ben 50", "mark that as paid": "X paid me back" is `from_participant` X and `to_participant` "@me" (the signed-in person), and "all of it" is the amount `settle_up_plan` recommends between the two. The person must have confirmed the amount, the currency, who paid and who was paid before this runs. State a payment in the currency it was paid in too: SplitPop converts it at its own rate, as the app's settle-up form does, and keeps both amounts -- never send a converted amount or a rate. ("Ben sent me 50 francs" in a euro group is `{"amount": "50.00", "currency": "CHF"}`; the answer shows the rate and the converted amount.) A payment is dated when it is recorded; it has no date of its own to set. Record only payments that actually happened. SplitPop records payments made elsewhere; it moves no money. The group's other members are notified, as their notification settings allow. The answer's `undo` is the `payments_delete` call that takes it back.
- Permissions
groups:read payments:write- Required arguments
group_id, from_participant, to_participant, amount, request_key- Result fields
payment object, possible_duplicate_of object | null, undo object, group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
payments_create_manyOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Show the planned transfers and wait for the person's yes before calling. Records several payments in one group in a single transaction: either every transfer lands or none does, so settling a trip can never leave a group half-settled. Answers "settle up", "we're square", "record the settle-up plan" -- after the person has seen the transfers from `settle_up_plan` (or `groups_summary`) and said which of them happened. Every transfer must be one that SplitPop currently recommends -- same payer, same recipient, same amount -- and SplitPop checks that against the plan as it stands at the moment of the write, not the one you read earlier. Send only the transfers that actually happened; leaving one out is fine, and anything not on the plan is refused with the current plan in the result's data. One yes covers the whole list; send `confirmed_with_person: true` only after it. At most 20 transfers in one call, and fewer in a group with many members, because every transfer notifies all of them; the refusal says how many that group allows. A settle-up plan is in the group's own `base_currency`: send each transfer's amount exactly as `settle_up_plan` or `groups_summary` gave it. Record a payment made in another currency with `payments_create`, which converts it. SplitPop records payments made elsewhere; it moves no money. The answer's `undo` lists one `payments_delete` call per payment.
- Permissions
groups:read payments:write- Required arguments
group_id, transfers, request_key- Result fields
payments object[], undo object[], group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
payments_deleteOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Deletes a recorded payment and reverses its effect on everybody's balances. Before calling, say exactly which payment -- who paid whom, and how much -- and ask once, even when the person asked for it by name; their yes covers it (send `confirmed_with_person: true`). The one exception is a payment the person already confirmed in the previous turn, after your read-back or on a SplitPop card. When their words fit more than one, name each with its amount and date in that question. When `groups_get` says your `my_role` is MEMBER, say in the same question that it cannot be brought back from here. Read it with `payments_get` first and send its `version` as `expected_version`. Afterwards tell the person exactly what was removed -- the answer's `deleted` holds who paid whom and how much -- and how it comes back: only a group owner or administrator can bring it back, with `payments_restore` (the answer's `undo`, and `undo.is_allowed` says whether the caller may make it). SplitPop records payments made elsewhere; it moves no money.
- Permissions
groups:read payments:write- Required arguments
group_id, payment_id, expected_version, request_key- Result fields
payment_id string, is_deleted true, version number, deleted object, undo object, group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
payments_getReadsThe tool's description, as the server sends it
Reads one payment between two people in a group: the amount, who paid whom, the note, and the most recent ten entries of the lifecycle history of the record itself (created, edited, deleted, restored). `history_has_more` says when there are older ones, which only the SplitPop app shows. SplitPop records payments made elsewhere; it moves no money.
- Permissions
groups:read- Required arguments
group_id, payment_id- Result fields
payment_id string, group_id string, amount object, entered_amount object, fx object | null, from object, to object, note string | null, paid_at string | null, created_at string, updated_at string | null, is_deleted boolean, version number, history object[], history_has_more boolean, url string | null
payments_restoreChangesThe tool's description, as the server sends it
Brings a deleted payment back and restores its effect on everybody's balances. Only a group administrator or its owner may do this; anyone else is refused. Read the payment with `payments_get` first and send its `version` as `expected_version`. The answer's `undo` is the `payments_delete` call that deletes it again. SplitPop records payments made elsewhere; it moves no money.
- Permissions
groups:read payments:write- Required arguments
group_id, payment_id, expected_version, request_key- Result fields
payment object, undo object, group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
payments_updateOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Changes a recorded payment and recomputes everybody's balances. Read it with `payments_get` first and send its `version` as `expected_version`; if anyone changed it in between, the write is refused and you read it again. Send only the parts that change; the rest is kept exactly as recorded. What it replaces is not kept in SplitPop, but the answer's `undo` is the `payments_update` call that puts the previous values back -- to the cent while the currency stays the same, and at today's rate after a change of currency (`is_exact: false`). The person must have confirmed the change. State a payment in the currency it was paid in too: SplitPop converts it at its own rate, as the app's settle-up form does, and keeps both amounts -- never send a converted amount or a rate. A payment keeps the rate it was recorded at while its currency stays the same, so a new amount in that currency is converted at that rate, as the app's edit form does; another currency is converted at today's rate, which SplitPop may fetch from its rate provider. SplitPop records payments made elsewhere; it moves no money. The group's other members are notified, as their notification settings allow.
- Permissions
groups:read payments:write- Required arguments
group_id, payment_id, expected_version, request_key- Result fields
payment object, undo object, group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
people_addChangesThe tool's description, as the server sends it
Adds people to a group: new names -- each with an e-mail address and an emoji if the person gave one, as the app's Add people block takes them -- and people you already share a group with, in one call. Either all of them are added or none is. People the person may already know are never linked or duplicated on a guess: `contact_person_ids` need `same_person_confirmed: true`, and a new name that exactly matches a contact needs `create_new_people: true`; without them nothing is added and the answer is the one question to ask the person, naming each match's group ("Mia from Ski trip and Jonas from Flat share — same people or new?"), and a `same_or_new_token` that the answers count with once the person has answered. Adding someone does not invite them to sign in; it gives the ledger a name to settle with. An e-mail address is stored with the person and sends nothing (`invitations_create` does). It reuses only a seat already in this group (one still in the group is refused as already there; an archived one comes back) or somebody you already share a group with, with or without an account; any other address becomes a new person of your own and is checked against nobody. Only a group administrator or its owner may do this.
- Permissions
groups:read people:write- Required arguments
group_id, request_key- Result fields
changed_participant_ids string[], identity_visible boolean, people object[], group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, matching_contacts object[], linked_people object[]
people_archiveChangesThe tool's description, as the server sends it
Takes someone out of a group's pickers without touching anything they are already part of. Their past expenses and payments stay exactly as they are, and the balances do not move. Reversible with `people_restore`. Only a group administrator or its owner may do this.
- Permissions
groups:read people:write- Required arguments
group_id, participant_id, request_key- Result fields
changed_participant_ids string[], identity_visible boolean, people object[], group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string
people_listReadsThe tool's description, as the server sends it
Lists everyone in a group with the id to use when referring to them, whether they are archived, and which one is the caller. Answers "who is in this group". Administrators also see each person's account link state: somebody with `is_linked_to_account: false` has no SplitPop account behind their name, so they cannot be reminded and are brought in with `invitations_create`.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, identity_visible boolean, people object[], group_url string | null
people_remindOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Asks SplitPop to send one person in a group a reminder of what they owe the caller, by e-mail and push, in that person's own language. Answers "remind Ben", "chase Anna for the money". The amount is SplitPop's own: the transfer its settlement plan currently recommends from them to the caller. If the plan has no such transfer — they are square, or they are the one who is owed — there is nothing to remind them of and this is refused. It is refused as well for somebody who has no SplitPop account yet, because there is nowhere to send it; invite them instead. And for somebody who has been removed from the group: their name stays in the ledger, but SplitPop no longer writes to them about it. The person must have asked for this by name: it puts a message in somebody else's inbox. Nothing is written to the ledger. One person may be reminded by one other at most 3 times a day in a group, and one account sends at most 20 reminders an hour.
- Permissions
groups:read chat:write- Required arguments
group_id, participant_id, request_key- Result fields
participant_id string, display_name string, amount object, notification_count number, group_id string, group_url string | null, is_replay boolean, confirmation "stated" | "not_stated"
people_restoreChangesThe tool's description, as the server sends it
Puts an archived person back into a group's pickers, so they can be named in new expenses and payments again. Only a group administrator or its owner may do this.
- Permissions
groups:read people:write- Required arguments
group_id, participant_id, request_key- Result fields
changed_participant_ids string[], identity_visible boolean, people object[], group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string
people_updateOverwrites, deletes or notifies othersThe tool's description, as the server sends it
Changes what a group calls someone, and the emoji shown beside them. This is the group's own name for that person: it does not change their account anywhere else, and the name it replaces is not kept. Only a group administrator or its owner may do this. The person must have asked for this change.
- Permissions
groups:read people:write- Required arguments
group_id, participant_id, request_key- Result fields
changed_participant_ids string[], identity_visible boolean, people object[], group_id string, my_position object | null, balances object[], group_url string | null, is_replay boolean, tell_the_person string, confirmation "stated" | "not_stated"
public_expense_getReadsNo sign-inThe tool's description, as the server sends it
Reads one expense without signing in, the way anyone with its link reads it in the SplitPop app: any expense of a public group (`public_ledger_list` lists their ids), or a single expense a private group shared on its own. Its title, note, date and category, the amount in the group's currency and as it was entered with the exchange rate behind it, who paid, how it is split and on what basis, and how many files are attached. Any other expense reads as not found.
- Required arguments
group_id, expense_id- Result fields
expense_id string, group_id string, group_name string, is_group_public boolean, title string, notes string | null, amount object, entered_amount object, fx object | null, occurred_at string, split_mode "EQUAL" | "SHARES" | "PERCENT" | "FIXED", category "GENERAL" | "EATING_OUT" | "GROCERIES" | "DRINKS_NIGHTLIFE" | "ACCOMMODATION" | "FLIGHTS" | "TRANSPORT" | "CAR_FUEL" | "ACTIVITIES" | "SHOPPING" | "GIFTS" | "HOME_RENT" | "UTILITIES_SUBSCRIPTIONS" | "HEALTH_WELLNESS" | "FEES_SERVICES", category_source "AUTO" | "MANUAL", payers object[], shares object[], attachment_count number, url string | null
public_group_getReadsNo sign-inThe tool's description, as the server sends it
Reads a group that has been shared publicly, without signing in, the way anyone with its link reads it in the SplitPop app: its name and currency, how many people, expenses and payments it holds, what has been spent, everybody's `participant_id` and net position (positive means the group owes them), the transfers SplitPop recommends to settle it, and where the money went (`spending_by_category`, the five biggest categories). No e-mail addresses and nothing about who has an account. Its expenses and payments are `public_ledger_list`, one expense in full is `public_expense_get`. A group that is not public reads as not found.
- Required arguments
group_id- Result fields
group_id string, name string, currency string, counts object, total_spent object | null, people object[], balances object[], recommended_transfer_mode "SIMPLIFIED" | "DIRECT", is_settlement_plan_ready boolean, recommended_transfers object[], spending_by_category object[] | null, url string | null
public_ledger_listReadsNo sign-inThe tool's description, as the server sends it
Lists the expenses and payments of a group that has been shared publicly, without signing in, newest first, one page at a time: what anyone with the group's link reads in the SplitPop app. The same filters and the same entries as `ledger_list` (versions, entered amounts, split modes, notes, file counts, and each person's part on a page filtered by `participant_id`), without `is_mine`. The people's `participant_id`s come from `public_group_get`, and one expense opens in full through `public_expense_get`. A group that is not public reads as not found.
- Required arguments
group_id- Result fields
group_id string, items one of 2 shapes[], search object | null, group_url string | null, next_cursor string | null, has_more boolean
searchReadsThe tool's description, as the server sends it
Finds the signed-in person's SplitPop groups and expenses whose name or title contains the query, in any language -- "find the taxi", "which group had the hotel" -- and returns each as a citable document with its web address. SplitPop has no full-text index, so the search covers the 10 groups the person joined most recently and the newest 20 expenses in each of them, up to 20 results; use `groups_list` and `ledger_list` to read further. Each hit says which `kind` it is and carries the fact behind it -- an expense's `amount`, `occurred_at` and `category` (one of SplitPop's fifteen category ids, with `category_source`), a group's `my_balance` -- so most questions are answered without a second call. Pass a returned id to `fetch` for the whole document.
- Permissions
groups:read- Required arguments
query- Result fields
results object[]
settle_up_planReadsThe tool's description, as the server sends it
Shows a group's settle-up plan as a card when the person asks to settle up or who owes whom: the transfers SplitPop recommends (who pays whom, how much, with both names), the caller's own net position, the group's transfer mode and its link; where the app shows cards, the card has a button the person can press to record every transfer as paid. Records nothing by itself. In the SIMPLIFIED transfer mode the plan keeps the number of payments low, so somebody may be asked to pay a different person than the one they owe directly. `is_settlement_plan_ready` is false while SplitPop is still reconciling the balances. Counts and spending are `groups_summary`, which shows no card. `recent_changes` lists the expense and payment changes of the last 15 minutes, newest first (ids, amounts, whether the caller made them, and `via`: "card" when the writing client reported a SplitPop card button through its request key, which SplitPop does not verify), so "what changed?" is answered from it.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, name string, currency string, my_position object | null, recommended_transfer_mode "SIMPLIFIED" | "DIRECT", is_settlement_plan_ready boolean, recommended_transfers object[], url string | null, recent_changes object[], writes_blocked object | null
stats_by_categoryReadsThe tool's description, as the server sends it
Answers "how much did we spend", "where did the money go". Totals what a group spent per category ("how much did we spend on food in March?", "what did Mia pay for transport?"), in the group's own currency: every expense was converted at its own day's rate when it was recorded, so the amounts add up as they are. Payments between people are never counted as spending. Without `participant_id` it is the whole group's spending. With `participant_id` it is that person's share of the expenses they are in (`basis: "SHARE"`, with what they paid towards those same expenses in `paid`); add `role: "PAYER"` for what they paid (`basis: "PAID"`) or `role: "BENEFICIARY"` for their share of the expenses with a share for them. Narrow the time with `month` or with `from`/`to`. Categories come biggest first with their share of the total as `percent`; a category that is not listed had nothing. The numbers match the statistics the app shows under the ledger for the same filters. `status: "PREPARING"` means SplitPop is still preparing this group's statistics and has no numbers yet; ask again a minute later. The numbers are exact for any period, in any time zone.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, status "READY" | "PREPARING", basis "GROUP" | "PAID" | "SHARE", participant_id string | null, from string | null, to string | null, total object, expense_count number, paid object | null, categories object[], group_url string | null
stats_by_monthReadsThe tool's description, as the server sends it
Answers "how much did we spend per month", "how did our spending develop this year?". Totals what a group spent in each calendar month, oldest first ("which month was the most expensive?"): the trend on the app's statistics page, in one call. Months are the person's own when `time_zone` names their IANA zone ("Europe/Zurich"), exactly as the app counts them in their browser, and UTC months without it. `from_month` and `to_month` ("YYYY-MM", both included) choose the window, at most 24 months; with only one of them it is 12 months from or up to it, and with neither it is the 12 months up to the month of the group's latest expense, the app's own default. A month with nothing spent is listed with zero. Payments between people are never spending and never counted; amounts are in the group's own currency. One month's categories or one person's part is `stats_by_category` with `month`. `status: "PREPARING"` means SplitPop is still preparing this group's statistics and has no numbers yet; ask again a minute later. The months are exact in any time zone.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, status "READY" | "PREPARING", time_zone string, from_month string, to_month string, months object[], total object, expense_count number, group_url string | null
stats_by_personReadsThe tool's description, as the server sends it
Answers "who spent the most?", "how much did each of us spend" and "what was everyone's share?" -- spending per person -- for a group in one call. For each person: their share of the expenses they are in (`share`, and `share_percent` of the total), what they paid towards those same expenses (`paid`), and how many expenses they are in -- the "by person" view of the app's statistics page, and for each person exactly what `stats_by_category` answers with their `participant_id`. Share and paid are spending, not a balance: payments between people are never counted, and `groups_summary` says who owes whom. Narrow the time with `month` or `from`/`to` (in the person's own calendar with `time_zone`), and the spending to one category with `category`. People come biggest share first; somebody with no expense in the period is not listed. The answer also carries the group's spending per category for the same period, and `span`: the dates of the group's first and last expense, all time, whatever the filters. Amounts are in the group's own currency. `status: "PREPARING"` means SplitPop is still preparing this group's statistics and has no numbers yet; ask again a minute later. The numbers are exact for any period, in any time zone.
- Permissions
groups:read- Required arguments
group_id- Result fields
group_id string, status "READY" | "PREPARING", from string | null, to string | null, time_zone string | null, category "GENERAL" | "EATING_OUT" | "GROCERIES" | "DRINKS_NIGHTLIFE" | "ACCOMMODATION" | "FLIGHTS" | "TRANSPORT" | "CAR_FUEL" | "ACTIVITIES" | "SHOPPING" | "GIFTS" | "HOME_RENT" | "UTILITIES_SUBSCRIPTIONS" | "HEALTH_WELLNESS" | "FEES_SERVICES" | null, total object, expense_count number, people object[], categories object[], span object | null, group_url string | null
Resources and prompts
For clients that assemble context without calling tools, a group and its first page of the ledger are also addressable as resources. The server sends its house rules with every handshake, as its instructions. Six prompts carry the longer walkthroughs a tool description must not: adding an expense from a sentence, splitting a receipt by who had what, settling up, a spending report, catching up across groups, and looking after a group's members and requests. Each one takes the group by the name you know it by, and a client's prompt menu offers your own groups as you type.
Resources
splitpop://group/{groupId}/ledgerThe first page of a group's ledger as JSON: its most recent expenses and payments, newest first. Use the `ledger_list` tool for further pages or filters.
splitpop://group/{groupId}A compact JSON view of one group: its name and currency, how many people, expenses and payments it holds, what has been spent and where the money went, the caller's net position and the transfers that would settle it.
Prompts
add_expense_from_textTurns a sentence such as "I paid 48 for dinner, split with Ana and Ben" into a SplitPop expense, after confirming the amount, currency, payer and split with the person.
catch_upTells the person where they stand across their SplitPop groups and what happened lately -- what they owe, what they are owed and what changed -- and changes nothing.
manage_groupWalks through the administration of a SplitPop group: who is in it, who is waiting to be, and what its link shows -- reading everything first and changing nothing without a decision from the person.
settle_upWalks through settling a SplitPop group: read who owes whom, present the recommended transfers, and record only what the person confirms happened.
spending_reportWrites a short report of what a SplitPop group spent in a period -- the total, the categories it went on, and who paid what against their share -- from SplitPop's own statistics, and changes nothing.
split_receiptWalks through splitting one bill by who had what: read the receipt's lines, ask who had which, spread tax and tip, preview SplitPop's own shares and record it as one expense once the person confirms.
A photo of a bill becomes a shared group
The flow the whole surface is shaped around. Four sentences from you, one write on our side, and in ChatGPT a second one that puts the photo on the expense.
- You hand your agent a photo of the bill and say: split this with Mia and Jonas.
- The agent reads the receipt itself and confirms the amount, the currency, who paid and how it is split with you.
- It calls groups_create_with_expense once. The group, the people and the first expense are written together or not at all, so there is no half-made group to clean up.
- Then the photo. If you attached it to your message in ChatGPT, ChatGPT hands the file itself to attachments_add_from_file, and SplitPop puts it on the expense. Claude sees the photo but cannot hand SplitPop the file, so the answer carries the expense's link, and you attach the receipt there in the app. A client that can give the agent the file's bytes attaches it with attachments_add_from_data instead.
- It answers with the group's link. You paste that into the chat with Mia and Jonas. Whether the link opens the whole group to them straight away, with no account, or only lets them ask to join is something the agent asked you before it created the group; you can ask it to change that later.
Limits
Generous enough that ordinary use never meets them, and low enough that a model stuck in a loop does.
- A list returns 20 items by default and at most 50. Every one of them is paginated with a cursor.
- 240 calls a minute per person, across every agent you have connected.
- 60 of those may change something.
- 600 calls a minute per agent registration, on a key the caller cannot choose; other signed-in agents are bounded by the per-person limit above.
- Calls made before anybody signs in — the tool list, the public reads, a sign-in challenge — never count against a signed-in person. They share one allowance of 600 a minute, and one address may use at most 120 of it, of which at most 30 may search a public group's ledger by title.
- An attachment is at most 5 MB and one of image/jpeg, image/png, image/webp, application/pdf.
- A request body is at most 1 MB, or 8 MB for the one tool that carries a file.
- Every tool call is logged with the client's name and version, the tool, how it ended and how long it took — never your token. Those rows are deleted after 90 days.
- A write's key is kept for 24 hours, with the answer that write produced. Repeating the key returns that first answer instead of writing again.
What an agent cannot do
Not a matter of permission: there is no tool for any of it. What can be done at all is done in the app, where the person doing it can see what it affects.
- Delete a group. SplitPop has no way to delete a group, in the app or here; a group goes only with the account of its last member.
- Delete your account. Only you can, in the app, on your profile page: it cannot be undone, so no assistant is ever asked to.
- Change a group's ledger currency. It reprices the whole group at once, and SplitPop only allows it while nothing has been recorded.
- Touch your profile, your notification settings or anything else about your account.
- Read or change a private group you are not in. Its link shows only its name and a way to ask to join, as in the app. A public group reads in full for anyone holding its link — signed out through the public tools, signed in as a guest through the ordinary read tools — and only its members change it.
- Import a ledger backup. Exporting one is a single read; importing is a long, locked, reversible process somebody has to watch.
If something goes wrong
- Claude says it couldn't reach the server, or connecting fails: check that the address is exactly https://site.splitpop.app/mcp, then remove the connector and add it again. You sign in with your own SplitPop account.
- The assistant asks you to sign in again, or a tool answers that it needs a signed-in account: connect SplitPop again — in Claude under Customize, Connectors, in ChatGPT at chatgpt.com/plugins — and sign in.
- Every call is refused with “Agent access is paused”: you paused it yourself. Resume it on your SplitPop profile page, under Connected apps.
- Every change is refused with “this SplitPop account has not yet accepted SplitPop's current terms”, while reading works: open SplitPop in your browser, accept the terms of use and privacy policy it shows above your groups, and ask again.
- A call is refused as too many requests: wait the number of seconds the answer names, then try again. The limits are above; ordinary use does not reach them.
- The assistant does not know a tool this page lists: clients keep their own copy of the tool list. In ChatGPT open SplitPop at chatgpt.com/plugins, then its Information panel, and choose Refresh; in Claude, disconnect the connector and connect it again.
- The assistant says it cannot use a group: paste the group's SplitPop link into the conversation. Every tool takes the link wherever it asks for an id, and a public group you are not in reads through it as well.
- An amount in another currency: an expense or a payment is recorded in the currency it was paid in, and SplitPop converts it at a rate it fetches itself. Only a settle-up plan is recorded in the group’s own currency, exactly as SplitPop worked it out.
- The receipt is not on the expense: SplitPop takes a file ChatGPT hands it from the conversation, so in ChatGPT attach the photo to your message. Where an assistant cannot hand SplitPop the file — in Claude it sees your photo but cannot — it gives you the expense’s link, and you attach the receipt there in the app.
Still stuck? Write to support@splitpop.app with the time it happened and the client you used.
Taking it back
Remove the connector in the client, and the agent has nothing left to call with. Removing SplitPop's access where you signed in ends it for every client at once: no further token is issued, and a short-lived one already handed out stops working as soon as it expires.
Taking a connection away never removes anything an agent wrote. Expenses, payments and messages stay in the group, and you edit or delete them in the app as you would your own.
Changelog
- 4 Oct 2026 — Withdraw a request to join. join_requests_withdraw takes back the signed-in person’s own request to join a group while it still waits for an owner or an administrator, as “Withdraw request” does in the app. It is quiet: nobody is notified, and the person may ask again.
- 25 Sept 2026 — Receipt photos from ChatGPT. A photo or a PDF you attach to your message in ChatGPT now reaches the expense through attachments_add_from_file. A file too large for it is refused in megabytes, with the app’s larger limit as the way to add it anyway.
- 25 Sept 2026 — Fewer calls for what people say out loud. A split may say “everyone”, with exceptions, and “@me” names the person the assistant acts for wherever a person is named; in a split by amounts, the people without one share the rest equally; and a bill divided by who had what — its lines, a tip, tax, a discount — is worked out to the smallest unit by SplitPop, which answers with who had what. The ledger and statistics filters take a name. A preview fetches its own exchange rate, says when the group already holds the same expense, and checks a receipt’s several lines at once. expenses_repeat adds an expense like an earlier one. A payment can be recorded in the currency it was paid in, converted the way the app’s settle-up form converts it. groups_update switches between the simplified and the direct settle-up plan. groups_join_options and join_requests_create ask to join a group, or to be one of its people. attachments_add_from_file takes a file ChatGPT hands over from the conversation and puts it on an expense. New people can come with an e-mail address and an emoji, and the activity list shows reactions.
- 25 Sept 2026 — Honest about money, links and receipts, and closer to what people say. An expense is described everywhere as what it is, stated in the currency it was paid in and converted by SplitPop, and a payment as recorded in the group's own currency. The server sends its house rules with every handshake. A SplitPop link pasted into the conversation works wherever a group, expense, payment or contact id is asked for. Every write that moves money answers with the exact call that takes it back, and says when the person's role cannot make it. Starting a group with its first expense, deleting a file or a message, renaming a person and revoking an invitation now take confirmed_with_person too, and changing a category is marked as the overwrite it is. A refusal names the role it needs, the reminder limit and how far a split is off. Six prompts, each usable from a client's menu by group name. The ledger export stays within what every client shows. And calls made before signing in are limited on their own, so they can no longer use up the allowance of people who have signed in. What the app shows is now one call away, too: spending by person and by month beside spending by category, all three in the person's own calendar; a deleted expense, and whether it can be brought back; your contacts; who owes you and whom you owe, person by person, across your groups; a search by title in the ledger, whose entries now carry what an edit or a delete needs; the spreadsheet export with the ledger's filters; and a public group in full without an account — its balances, its ledger and any of its expenses — as its link shows it in a browser.
- 24 Sept 2026 — Categories and spending. Every expense is filed under one of fifteen categories, chosen by SplitPop from its title or picked by the person: categories_suggest says what SplitPop would pick, expenses_set_category changes it and nothing else, and stats_by_category answers what a group spent on what — for a month, a period or one person.
- 22 Sept 2026 — The things people ask an assistant for rather than the things the app has screens for. An expense can now be worked out before it is recorded, and several lines of one receipt written together or not at all; a whole settle-up is recorded as one act, against the plan as it stands at that moment; a deleted expense can be brought back; an expense can be entered in another currency, converted with the rate SplitPop fetched itself; you can see what you are owed across all your groups, one total per currency and never added together; a group can be started with the same people as another one; a group's ledger also downloads as a spreadsheet; the activity list answers what changed since a moment, and says which record each entry is about; one person can ask SplitPop to remind another of what they owe; every write that moves money or changes who is in a group now takes a confirmed_with_person flag and says back whether it was stated; adding an expense or a payment says when the group already holds something very like it; and the cross-group overview reads twenty-five groups rather than ten and names that window when it reaches it.
- 21 Sept 2026 — First version, then a second the same day. Reading groups, people, members, invitations, requests, balances, the ledger and the activity; writing groups and their visibility, expenses, payments, people, member roles, invitations, decisions on requests, chat and attachments; exporting a group's ledger; two resources, three prompts, and one all-or-nothing sign-in, because the sign-in service issues no permission of SplitPop's own.
Privacy and terms
What SplitPop does with the data an assistant reads and writes, and how long it keeps the call log: Privacy Policy, AI assistants. What you are responsible for when an assistant acts for you: Terms of Use, AI assistants. Who operates SplitPop: Imprint.
Questions
Something not working, or a client this page does not mention? Write to support@splitpop.app.
Reporting a security problem
Found a security problem in SplitPop — in the app, in this server or anywhere else at splitpop.app? Write to support@splitpop.app with “Security” in the subject line. Test only with your own account and your own groups, and give us the chance to fix it before you tell anyone else.