Skip to content

Authentication

Requires the Business API add-on

The Business API is available only with the Business API add-on, switched on from Billing in your dashboard. It is an add-on, not a different plan: your locations bill exactly as they do now. What it includes.

Every request carries a shop-scoped API key as a bearer token.

http
GET /api/public/v1/me HTTP/1.1
Host: api.tesserapp.eu
Authorization: Bearer tsk_live_dP4y…

There is no login endpoint, no refresh token and no user identity. A key belongs to a shop, and it is created in the dashboard by a person who is signed in — a key cannot create another key. That is on purpose: a stolen key must not be able to mint its successor.

Status

tsk_ keys and the routes below ship with the Business API. The credential model they conform to — prefixes, hashing, one-time display, expiry, overlapping rotation, per-key rate limits, suspension without loss — is implemented and tested today.

Two key classes, and the 401 that tells you which

PrefixUsed forPresented to
tsk_live_… / tsk_test_…The REST APIAuthorization: Bearer on /api/public/v1/*
tmk_live_… / tmk_test_…The MCP connectorAuthorization: Bearer on /mcp

Present the wrong class and the 401 names the mistake — "this is an MCP key; the REST API expects a key beginning tsk_" — rather than saying "invalid credentials" and costing you an afternoon. Being specific leaks nothing: the caller already holds the key.

The key itself

  • tsk_live_ + 43 characters of base64url, from 256 bits of randomness. Nothing about it is derived from your shop, so nothing about it is guessable.
  • Add the prefixes to your secret scanner. tsk_ and tmk_ exist so a leaked key is greppable — in a paste, a log, a repository, a support attachment. If you run secret scanning (GitHub, GitLab, gitleaks, trufflehog), a rule for tsk_(live|test)_[A-Za-z0-9_-]{43} costs one line and catches the leak before we do.
  • Shown once. The plaintext is returned exactly once, when the key is created. No endpoint can re-read it — we store only a hash. Lose it and you rotate; there is no recovery, by design.
  • Named. A name is required, because an unnamed key is one nobody dares revoke. Name it after the system that will hold it: "Odoo nightly sync", not "test".
  • Identifiable without being usable. The dashboard and support see the first twelve characters, so "which key is this" is answerable from a log line.

Expiry: "never" is a real choice

Expiry is optional, and "never" is supported rather than tolerated. A CRM integration that dies silently at 3am is worse than a long-lived key that is named, audited and revocable in one click.

The dialog offers 30 / 90 / 365 days and "never", and does not default to "never" — so the choice is made deliberately, in either direction. Pick a finite expiry when a key is for a migration, a contractor or a trial; pick "never" for the integration your business runs on, and rely on rotation and revocation instead of a deadline you will not remember.

Rotation overlaps, so rotate before you cut over

Rotating a key issues a new key and puts the old one on a countdown instead of killing it instantly. Both work during the overlap window; the response tells you the old key's cutoff, and the dashboard shows the countdown.

The order that works:

  1. Rotate. Store the new plaintext.
  2. Deploy the new key to your systems.
  3. Verify traffic is flowing under the new key.
  4. Let the old one lapse — or revoke it early once you are sure.

The overlap is a deployment setting (see Rate limits and quotas); setting it to zero retires the old key immediately, for the case where a key is known to be compromised. Rotation of a webhook signing secret is a hard cutover today — see Webhooks → Rotating a secret.

Revocation, and the panic button

  • Revoke a key and it stops working on the very next request. There is no cache that outlives it. The row is kept, not deleted — the audit trail is the point.
  • Revoke all keys in one action, for the day a laptop goes missing. It is the action you want to already exist when you need it, so it does.
  • Create, rotate, revoke and scope changes are each audited with who did it. API writes are audited as the key that made them, so "the CRM did it" is legible in the shop's history.

Scopes

Scopes are coarse on purpose. Fine-grained scopes on a v1 API age badly, and a scope nobody understands gets granted "just in case".

locations:read   locations:write
programs:read    programs:write
cards:read       cards:write
transactions:read
analytics:read
webhooks:read    webhooks:write

There is no transactions:write, no key-management scope, and no staff or device scope. See what the API deliberately does not do.

Grant the narrowest set that works. A nightly reporting job wants transactions:read analytics:read and nothing else; a badge-provisioning integration wants cards:write cards:read programs:read.

Location restriction

A key can be restricted to one or more locations. The restriction applies to reads as well as writes — a one-site key cannot read another site's cards or transactions, which is the half integrators expect to be missing and it is not.

Analytics is the exception worth knowing before you build a dashboard on it: the endpoints that derive from transactions are recomputed for your locations, and the ones that derive from cards refuse rather than quietly widen to the whole shop. See location_scope_unsupported in Errors.

Debug a 403 with one call

bash
curl -s https://api.tesserapp.eu/api/public/v1/me \
  -H "Authorization: Bearer $TESSERAPP_API_KEY"

GET /me returns the shop, the plan and entitlements, the location counts — and the calling key's own scopes and location restriction. If you are getting a 403, this is the first call to make and usually the last.

Storing the key

  • Environment variable or a secret manager. Never in the repository, never in a frontend bundle — a browser cannot hold this key safely, because any visitor would then hold it too.
  • One key per integration. The rate limit is per key, so a runaway nightly job cannot lock out your live provisioning flow — but only if they are different keys.
  • Never log it. We redact credentials on our side; do the same on yours.

Requires the Business API add-on.