Appearance
TesserApp Integration API
Requires the Business API add-on
Everything on these pages — the REST API, webhooks and the MCP connector — is available only with the Business API add-on — one flat monthly line on the subscription you already have. API keys cannot be created without it, and existing keys stop authenticating if it lapses. The current price is quoted by the pricing endpoint, never written down on a page that can go stale.
It is an ADD-ON, not a different plan: your locations bill exactly as they do now, and switching it off never costs you a location.
Switch it on from Billing in your TesserApp dashboard. You are welcome to read all of this first — it is published so you can decide whether TesserApp fits your systems before you pay for anything.
Issue cards from your own systems, read what happened at the till, and get told about it as it happens. One shop, one set of credentials, no user accounts to manage on our side.
https://api.tesserapp.eu/api/public/v1Status of this reference
The API-plan surface is being built. Pages here mark what is live today against what ships with the Integration API, because a reference that quietly describes unbuilt behaviour is worse than one that admits the difference.
Live today: webhooks (HMAC-signed delivery, retries, the delivery log) and the REST surface, which is built and mounted. Still landing: the dispatch sites for the newer webhook events (the event table says which, generated from the code), the rendered reference, and the MCP connector.
Start here
| Quickstart | Key → GET /me → program → card → webhook → a real card.used. Fifteen minutes. |
| Authentication | tsk_ keys, scopes, location restriction, rotation, revocation. |
| Errors | The generated error catalogue, and which failures are recoverable. |
| Rate limits | Per-key limits, Retry-After, and what to do when you hit one. |
| Idempotency | Safe retries, and the 409 that means you reused a key. |
| Pagination | Cursors, and why there is no ?page=. |
| Plan limits | What the integration module includes, and what it costs. |
| Reference | Every endpoint, generated from the server's own schemas. |
| Webhooks | The event catalogue, the delivery rules, and the identity question. |
| Integration kit | Tested verification and retry snippets in Node, PHP and Python. |
| MCP connector | Point your own LLM at your shop's data. |
| Changelog | Dated, additive changes to /v1. |
What it is
- REST over JSON. Bearer-authenticated with a shop-scoped API key. No cookies, no CSRF, no OAuth dance, no user accounts.
- A management and read plane. You create locations, programs and cards, you read transactions and analytics, you manage your own webhooks.
- Webhooks for everything that happens at the till, signed, retried and deduplicable.
- An MCP connector so a merchant can point their own LLM at their own data.
What it deliberately does not do
Read this section before you design against the API. Each of these is a decision, not a missing feature — if you go looking for the endpoint you will find this paragraph instead of a 404.
No transaction writes. Ever.
There is no endpoint that awards a stamp, redeems a prize, moves a balance or reverses a transaction, and there is no transactions:write scope. A test in our codebase fails if one is ever added.
A transaction is the record that a real card was presented at a real place. If an API key could mint one, the entire transaction log would become assertable by whoever holds the key — including anyone who ever leaks it — and the loyalty history would stop being evidence of anything. The dashboard cannot write a transaction either: every transaction write in the platform is authenticated as a paired device. The API is being kept consistent with that, not cut back.
What you do instead: create the card through the API, let it be scanned on the shop app, and receive the transaction as a webhook. That round trip is the supported design, not a workaround.
Not in v1
| Not available | Why, and what to do |
|---|---|
| Program delete | Destructive and unrecoverable; dashboard-only. Unpublish or suspend a program over the API instead. |
| Image upload (program logo, access image) | Multipart upload is v2. Supply access_image_url by URL at create time. |
| Staff and device management | PIN issuing and device pairing are physical-world operations at a counter. |
Location DELETE, renewal blocks, device release | Billing and hardware-lifecycle actions stay owner-initiated in the dashboard. |
| NFC switches | The feature is off; we do not expose routes for a switched-off feature. |
Card /activate and /claim | The consumer app's unauthenticated enrollment routes, not an integration surface. |
| Analytics for a location-restricted key, on some endpoints | Three analytics endpoints derive from transactions and can be narrowed to one location; four derive from cards or mix the two and would return a shop-wide number. Those return location_scope_unsupported rather than a wrong figure. See Errors. |
No SDKs, and that is deliberate
We do not publish client libraries, and we do not intend to.
A roughly forty-endpoint, bearer-authenticated CRUD API over JSON is precisely the case where a hand-maintained client adds cost without adding help:
- it lags the API — the day we ship a field, the SDK does not have it;
- it becomes its own support surface, in five languages we do not test in;
fetch, Guzzle andrequestsalready do the job, and your team already knows which one it uses.
So instead of a library we ship two things that keep working when we are asleep:
- Generated clients — a typed client for TypeScript, PHP or Python from our OpenAPI document, with the exact command. It is always current, because it is generated from the document CI verifies against the live routes. We maintain nothing.
- A tested integration kit — signature verification and idempotent retries in Node, PHP and Python. These are the two places integrations genuinely break, and every snippet is executed by our test suite against real generated payloads, so a snippet that stopped being correct fails our build rather than your integration.
If the generated route turns out not to be enough in practice, we will publish a client on that evidence. Not before.