sumup-cli
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@sumup-cliexport my product catalog as CSV"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
sumup-cli
English · Deutsch
CLI and MCP server for SumUp: catalogue, stock, sales, payouts and bulk product edits, including the things the official API does not expose at all.
One TypeScript core, two thin wrappers over it:
src/cli/command line, for scripts and cronsrc/mcp/MCP server, for use inside Claude and other MCP clients
Built and tested against a live Swiss kiosk account of roughly 650 items.
Not affiliated with SumUp. Half of what this tool does rides on the undocumented internal API behind the merchant dashboard, which SumUp can change or break at any time without notice. It reads your own account with your own credentials, and it will happily edit your live catalogue if you ask it to. Keep an export around before you bulk-edit anything. MIT licensed, no warranty.
The two halves
SumUp has a documented public API and an undocumented internal one, and the things you want live on both sides.
What | Where | Auth | Stability |
Merchant profile, transactions, line items, payouts |
|
| Documented and versioned |
Catalog: items, prices, cost prices, SKUs, stock, categories, taxes |
| Browser session cookie | No compatibility promise |
There is no product or inventory endpoint anywhere in the public API, which is why the catalog half rides on a logged-in dashboard session.
Two things that cost an hour each if you forget them
Every internal call needs
accept-version: 4.0.0. Without it the upstream returns404, which reads like a wrong path but is not.Auth is the session cookie against the same-origin Next.js proxy, not a bearer token to
api.sumup.com.
Both are encoded in src/core/session/endpoints.ts,
where every path records a verified / unverified status and the date it was
last observed working.
Related MCP server: Connhex MCP Server
Data quirks worth knowing
Money is in minor units.
value: 290is CHF 2.90,cost_price.value: 144is CHF 1.44.tax_rateis percent times 1000.8100means 8.1 percent,2600means 2.6 percent.Margin is computed on the net price, not the gross one. SumUp's own "Gewinn" and "Marge" for a 2.90 gross / 2.68 net / 1.44 cost item read CHF 1.24 and 46.3 percent. This tool matches that.
SKU and stock are not in the item list. The item search has prices but no SKU or stock; the inventory search has SKU and stock but no prices.
catalog exportjoins them onvariant_id.Stock goes negative. SumUp lets a count fall below zero, which just means sales were rung up past an empty shelf. Treat it as data, not as an error.
Rows are per variant, not per item. An item with two variants becomes two rows, so the row count is always at least the item count.
Setup
git clone https://github.com/oggii/sumup-cli.git
cd sumup-cli
npm install
npm run build # then: node dist/cli/index.js <command>
npm link # optional, puts `sumup` on your PATHWithout the link step, npx tsx src/cli/index.ts <command> runs it straight
from source. The examples below say sumup; substitute whichever you use.
Nothing about your account travels with the repository. Credentials, the browser
profile and the session cookie all live under ~/.sumup-cli, so a second
machine starts empty and you set it up there once.
Catalog access (session)
sumup auth capture --login # opens a browser once, you sign in
sumup auth capture # afterwards, headless, mints a fresh tokenThe dashboard's access token lives about 15 minutes. Loading the dashboard
exchanges the long-lived refresh cookie for a new one, so the headless refresh
keeps working for as long as SumUp keeps the profile signed in. The cookie is
written to ~/.sumup-cli/session-cookie.txt with mode 600.
sumup auth status prints exactly how many seconds are left.
Storing the sign-in
The token lapses every 15 minutes, so on a browser that cannot refresh headless
you end up typing the password all day. Store it once and auth capture --login
fills the form itself:
sumup auth credentials --email you@example.com # hidden prompt for the password
sumup auth credentials # show what is stored
sumup auth credentials --clear # forget itThe password is read from a hidden prompt and handed to the OS on stdin, so it
reaches neither your shell history nor the process list. Where it ends up
depends on the machine: Windows DPAPI tied to your Windows account, the
macOS keychain, the Linux Secret Service via secret-tool, or, if none
of those exist, a 0600 file that the CLI warns you about. SUMUP_EMAIL and
SUMUP_PASSWORD override the store for a single run.
Autofill fills the address and the password and presses the real submit button,
skipping the passkey button that sits next to it and carries the same
type=submit. Note that SumUp shows both fields at once: filling only the
password posts an empty username and the form simply comes back, so both are
always filled. If the page rejects the login it says so instead of retrying.
Anything the flow still insists on, a Cloudflare challenge or a two-factor code,
is yours to finish in the visible window and the command waits for it. On an
account without either, auth capture --login runs unattended end to end.
--no-autofill skips the whole thing.
The headless refresh depends on which browser the profile runs on. A real
Chrome or Edge gets through; Brave does not, because Cloudflare holds the
auth redirect on a headless Brave, so there auth capture needs --login and a
visible window every time the token lapses. Either way a signed-in profile is
still redirected via auth.sumup.com to trade its refresh cookie, so the code
waits for that bounce to settle rather than reading the URL straight after
navigation and wrongly concluding it is logged out.
playwright-core is used deliberately: it ships no browsers, and reuses a
Chromium build already on the machine instead of pulling a 150 MB download.
Point SUMUP_CHROMIUM_PATH at a binary if none is found.
Public API access (key)
The key SumUp shows you by default is a public key (sup_pk_*) and their
docs say not to use it. It returns 401 on /v0.1/me. You need a secret key:
me.sumup.com → profile → For Developers → Toolkit → API Keys → Create
Copy it immediately, SumUp does not store it. Then:
sumup auth login --api-key sup_sk_xxxxxUsage
sumup auth status # credentials, session expiry, endpoint health
sumup auth credentials --email you@example.com # store the sign-in for autofill
# Catalog (session only, no API key needed)
sumup catalog export -f csv -o out/inventar.csv # one row per variant, price/cost/margin/stock
sumup catalog export -f csv --all-columns
sumup catalog native-export -o out/sumup.csv # SumUp's own 47-column CSV
sumup catalog validate out/sumup.csv # check an edited file before import
sumup catalog restock --sku 1-0004=48 --sku 1-0008=48 -o out/lieferung.csv
# book a delivery, stock only
sumup catalog import out/lieferung.csv --yes # upload it through the dashboard
sumup catalog categories
sumup catalog stock --low # at or below the low-stock threshold
sumup catalog stock --negative # sold past zero
sumup catalog taxes
sumup catalog item <item_id> # full raw payload
# Download Center reports, all ten (session only)
sumup reports list
# range reports, --from / --to
sumup reports get sales --from 2026-08-01 --to 2026-08-17 -o out/verkaeufe.csv
sumup reports get transactions --from 2026-08-01 --to 2026-08-17 -o out/transaktionen.csv
sumup reports get cashbook --from 2026-08-01 --to 2026-08-17 -o out/kassenbuch.csv
sumup reports get items --from 2026-08-01 --to 2026-08-17 -o out/artikel.csv
sumup reports get invoicing --from 2026-07-01 --to 2026-07-31 --doc-type invoices
sumup reports get revenue --from 2026-08-01 --to 2026-08-17 # PDF
sumup reports get fiscal --from 2026-08-01 --to 2026-08-17 # KassenSichV zip
# monthly statements, --month (or --day for a single date)
sumup reports get payouts --month 2026-07 # Auszahlungsbericht PDF
sumup reports get fees --month 2026-07 # Gebührenabrechnung PDF
sumup reports get payments --month 2026-07 # Zahlungsbericht PDF
sumup reports get payments --month 2026-07 --format xls # same as legacy .xls
sumup reports get payouts --day 2026-07-15
# Profit
sumup profit --from 2026-07-01 --to 2026-07-31
sumup profit --from 2026-07-01 --to 2026-07-31 --by-item -f csv -o out/marge.csv
# Umsätze and Auszahlungen (session only, no API key needed)
sumup sales list --from 2026-08-01 --to 2026-08-17 -f csv -o out/aug.csv
sumup sales movers --from 2026-08-01 --to 2026-08-17
sumup sales payouts --limit 30
# Same data via the public API (needs the secret key)
sumup transactions list --from 2026-08-01 --to 2026-08-17 -f csv
sumup transactions items --from 2026-08-01 --to 2026-08-17 -f csv
sumup payouts list --from 2026-07-01 --to 2026-07-31 --native-csv
sumup endpoints # what is mapped and what is verifiedreports get sales is the itemised bookkeeping export: one row per line item
with Datum, Transaktionsnummer, Zahlungsmethode, Beschreibung, Kategorie, Artikelnummer, Preis (brutto), Preis (netto), Steuer, Steuersatz. Column
headers follow --locale, so pass --locale en-GB for English.
All ten Download Center reports are wired up. Output type is detected from the
response, so PDFs, legacy .xls and zips are written as bytes while CSVs get a
UTF-8 BOM for Excel. Pass -o or a file is named automatically under out/.
There are deliberately two routes to sales and payouts. The sales group uses
the dashboard session and works today with no key at all. The transactions
and payouts groups use the documented public API, which is stabler and
suitable for cron, but needs a sup_sk_ secret key.
CSV output is semicolon-separated with a UTF-8 BOM, so Excel on a Swiss locale opens it with umlauts and emoji intact and no import dialog.
How profit is calculated
sumup profit combines two reports, because neither has both sides:
Source | Contributes |
| revenue, and Gewinn = net-of-VAT revenue minus cost price |
transactions export | the card fees SumUp charges |
VAT needs no subtraction: SumUp already computes Gewinn on the net price.
Three traps, all found by reconciling against SumUp's own figures:
The transactions report lists every card payment twice, once as
Zahlungand once asAuszahlung, carrying the same fee. Summing blindly doubles the fees. OnlyZahlungrows count.That report covers card payments only. Cash never appears in it, so total revenue comes from the item report and no fee applies to cash.
Items without a cost price report a blank Gewinn. They are surfaced as
revenueWithoutCostrather than being counted as pure profit or pure loss.
The result is an operating contribution, not a final Nettogewinn: it is before rent, wages, and anything in the Ausgaben module.
Editing products
Use the CSV round trip. It is SumUp's own bulk-edit mechanism, so it needs no reverse-engineered write endpoint:
sumup catalog native-export -o out/sumup.csv # 47 columns, one row per variant
# edit prices, cost prices, SKUs, stock, categories in Excel or a script
sumup catalog validate out/sumup.csv # catch problems before SumUp doesThen upload it, either with Importieren on the Artikel page or with sumup catalog import (below). Never touch the Item id (Do not change) or
Variant id (Do not change) columns; that is how SumUp matches rows back to
records.
Booking a delivery
The common case is not a free-form edit, it is a supplier invoice: n cartons arrived, raise the stock, change nothing else. That is one command.
sumup catalog restock --sku 1-0004=48 --sku 1-0014=48 \
--sku 1-0008=48 --sku 1-0002=48 \
-o out/lieferung-1808.csvbase: live export, 646 items
1-0004 Coca-Cola Zero 0.5L PET 34 + 48 -> 82
1-0014 Valser Kohlensäure 0.5L PET 14 + 48 -> 62
1-0008 Evian 0.50L PET 26 + 48 -> 74
1-0002 Coca-Cola Zero 0.33L DOSE 7 + 48 -> 55Four things it does on purpose:
Only the Quantity cell moves. An item that already exists is never re-priced on a restock, even when the supplier's net price has drifted. Cost and selling price are carried across unchanged.
Stock is read live, so the delivery lands on top of what the catalogue says now rather than on an export from last week.
--base <file>overrides that when you already have a fresh export in hand.The output is a partial file, header plus only the touched rows. SumUp matches by
Item id, so the other 680-odd variants stay out of the transaction entirely and nothing can be clobbered by a stale column.Untouched bytes stay untouched. Rows are spliced, not re-serialised, so SumUp's own quoting survives, including the trailing-space item names it quotes and a plain CSV writer would not. Output is LF, no BOM, exactly what the exporter emits.
Anything it cannot book safely is reported and skipped rather than guessed at:
a SKU that is not in the catalogue, a SKU sitting on more than one row (which
really happens: two different products typed with the same SKU), or an item with
inventory tracking off.
--dry-run shows the table without writing, --set treats the numbers as the
resulting stock instead of as a delivery, and the result is run through
validate before it is written.
Uploading it
sumup catalog import out/lieferung.csv --dry-run # open the flow, upload nothing
sumup catalog import out/lieferung.csv --yes # actually importThere is still no import endpoint to call, so this drives the dashboard's own
dialog in a browser: Weitere Optionen in the toolbar, the Import entry
in that menu, the file input behind it, then SELECTORS.IMPORT.CONTINUE_BUTTON.
SumUp ships those data-selector attributes itself, which survive translation
and class-name churn, so the flow is driven by them rather than by button
labels. Note that every product row also has an "Aktionen" button; matching on
that text hits a row menu instead of the toolbar.
Three things worth knowing:
It needs a visible window unless the profile runs on a real Chrome or Edge, since Cloudflare will not let a headless Brave through the auth bounce.
--headlessis there for the browsers that manage it.Without
--yesit degrades to a dry run. An import mutates a live catalogue, so silence is not consent. The file is validated before the browser is even started.The dialog says nothing on success, so the command reads the catalogue back afterwards and checks it now says what the file said. That check is the actual confirmation;
--no-verifyturns it off.
Verified end to end on 2026-08-18 by importing a one-row file, reading the change back from the live catalogue, and importing the original value again.
The direct per-item write API is still not enabled. The read endpoints were
mapped from real traffic, but the write shape was never captured, and both the
CLI and the MCP tool refuse rather than firing a guessed PUT at a live
live catalogue.
To enable direct writes, save one product in the dashboard while capturing
traffic, then run sumup discover on the capture and fill in
src/core/session/endpoints.ts. Writes would then still be dry-run by default,
needing --yes (CLI) or confirm: true (MCP).
Re-mapping the API when SumUp changes it
Log in at me.sumup.com, DevTools → Network → tick Preserve log
Click through the screens you care about
Right-click the request list → Save all as HAR with content
sumup discover capture.har --catalog-onlyIt groups traffic by method and path template, collapsing ids, and reports
query parameters, request body keys and response shape. A HAR contains a live
session token; .gitignore already excludes *.har.
Sample payloads from the 2026-08-17 mapping are in captures/ (gitignored).
MCP server
{
"mcpServers": {
"sumup": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/sumup-cli/src/mcp/server.ts"]
}
}
}17 tools:
Tool | Needs |
| nothing |
| session |
| session |
| session, or none with |
| signed-in browser profile, plus session to verify |
| session |
| secret key |
| secret key |
| refuses, see Editing products |
sumup_catalog_stock with low: true pairs well with sumup_sales_list for
restocking decisions, and sumup_catalog_restock turns the resulting order into
an import file once it arrives.
Full API map
docs/api-map.md documents the whole surface discovered by
walking every dashboard page: roughly 60 endpoints across catalog, sales,
payouts, cash management, customers, members, expenses, online store,
invoicing and payment links, plus the unit conventions and the known gaps.
Notes
Node 20 or newer, uses built-in
fetch.The official
@sumup/sdkis deliberately not used: it is still marked subject to breaking changes, and the internal half needs a custom HTTP layer anyway, so both halves share one client insrc/core/http.tswith retry and rate-limit backoff.Never commit
.env,.session-cookie.txt,*.har, orcaptures/. A HAR file and a session cookie both contain a live token for your account.
Contributing
Issues and pull requests are welcome, particularly for endpoints this has not
mapped, other locales, and dashboard changes that break a selector. If SumUp
moves something, sumup discover on a fresh HAR is the fastest way to find out
what, and src/core/session/endpoints.ts is where the answer belongs.
Licence
MIT, see LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
Connect Exact Online to your AI assistant via MCP. Manage Exact Online with natural language.
- PressoOAuthnow.presso
Connect e-commerce and marketing data to AI assistants via MCP.
Hosted MCP for e-commerce: live product catalog, stock, and pricing for AI agents.
Related MCP Servers
AlicenseNot gradedqualityDmaintenanceEnables natural language interactions with SumUp payment services and APIs, allowing users to manage their SumUp account, process payments, and access payment data through LLM-powered conversations.91 npm7Apache 2.0- AlicenseBqualityBmaintenanceEnables natural language interaction with Connhex IoT platform APIs for managing devices, telemetry, rules, and resources via MCP tools.301MIT
- AlicenseCqualityCmaintenanceEnables natural language interaction with the WeFact invoicing platform, allowing users to manage debtors, invoices, products, subscriptions, and perform various administrative tasks via MCP-compatible clients.181AGPL 3.0
- FlicenseNot gradedqualityDmaintenanceEnables natural language interaction with SimplyBook.me API to manage bookings, services, and performers through MCP.2-