Skip to content

Authentication

All API calls send Authorization: Bearer <token>. There are three kinds of credential.

Credential Prefix Obtained by Bound to Use for
OAuth access token tps_at_live_ / tps_at_test_ Authorization code + PKCE One business, the scopes it granted Apps that businesses connect
Client-credentials token tps_at_… grant_type=client_credentials Your application; reaches only businesses that connected it Platform connectors, background jobs
Business API key tps_sk_live_ / tps_sk_test_ Business → Integrations → API keys One business A business's own website or scripts

Access tokens last 1 hour. Refresh tokens last 90 days and rotate: each refresh returns a new refresh token and invalidates the old one.

Authorization code with PKCE

  1. Create a PKCE pair (code_verifier, code_challenge = BASE64URL(SHA256(verifier))).
  2. Send the business owner's browser to:
https://pos.techtursolutions.com/oauth/authorize?response_type=code
  &client_id=…&redirect_uri=…&scope=products:read%20orders:write
  &state=…&code_challenge=…&code_challenge_method=S256

The owner signs in, chooses which of their businesses to connect (only businesses where their role holds integrations.manage), and approves the scopes. Sandbox apps can only be connected to sandbox businesses and live apps to live businesses.

  1. You receive redirect_uri?code=…&state=… (or error=access_denied). Check state.
  2. Exchange the code (form-encoded):
curl -X POST https://pos.techtursolutions.com/api/oauth/token \
  -d grant_type=authorization_code -d client_id=$CLIENT_ID \
  -d code=$CODE -d redirect_uri=$REDIRECT_URI -d code_verifier=$VERIFIER
{ "access_token": "tps_at_live_…", "token_type": "Bearer", "expires_in": 3600,
  "scope": "products:read orders:write", "refresh_token": "tps_rt_live_…", "business_id": "cm…" }

Codes are single-use and expire after 5 minutes. Replaying a used code revokes every token it produced (RFC 6749 §4.1.2). The redirect_uri must exactly match one registered on the application; registered URIs must be https, except http://localhost for development.

Refreshing

curl -X POST …/api/oauth/token -d grant_type=refresh_token -d client_id=$CLIENT_ID -d refresh_token=$RT

If the business has disconnected your app, refresh fails with invalid_grant.

Client credentials

curl -X POST …/api/oauth/token -u "$CLIENT_ID:$CLIENT_SECRET" -d grant_type=client_credentials -d scope="platform:identity products:write"

The token carries no business. When you call a business route, POS checks that the business has an active connection to your application and uses the intersection of the token's scopes and the scopes that business granted.

Revoking

POST /api/oauth/revoke with token, client_id, client_secret. Revoking the application in the console, or a business disconnecting it, revokes its tokens immediately.

Scopes

Scope Allows
business:read Business profile, locations, registers
products:read / products:write Catalogue read / create, update, sync
inventory:read / inventory:write Stock levels / stock adjustments
orders:read / orders:write Orders and sales / submit and update orders, refunds
customers:read / customers:write Customers
reports:read Sales summaries; also lets a token list all sales, not just its own
webhooks:manage A business's own webhook endpoints
platform:identity Mint launch tickets (platform connectors only)
platform:businesses Resolve linked businesses (platform connectors only)
platform:metrics Report platform metrics (platform connectors only)

Dashboard-only routes (accounting, staff, settings) are never available to tokens.

Secrets

Client secrets are stored as Argon2id hashes and shown once. Rotate them in the console (the old secret stops working at once). Never put tokens in URLs, front-end bundles or logs.