TouricalDevelopers

Authentication

Bearer tokens, scopes, storage, and rotation.

The Tourical API uses Bearer tokens that you generate in the operator dashboard, under Settings → Developer access. Each token is bound to one tenant and one set of scopes.

Token format

tk_<env>_<26 alphanumeric chars>
  • tk_ is the human prefix — useful in code grep and secret-scanning rules.
  • <env> is live for production-issued tokens, test for sandbox.
  • The body is 26 base32 characters carrying 130 bits of entropy. That's well above the practical guess threshold; you don't need to additionally restrict by IP.

Example: tk_live_a3f9bc4n7vqzr2k8mp5tx9w1

The dashboard only ever shows you the full plaintext once. After that, only the first ~12 characters are stored for UI hints. There is no recovery path — if you lose the plaintext, revoke and re-create.

Sending the token

Pass it as a Bearer token on every request:

Authorization: Bearer tk_live_a3f9bc4n7vqzr2k8mp5tx9w1

Missing, malformed, revoked, or expired tokens all return 401 Unauthorized as problem+json, with WWW-Authenticate: Bearer and a detail string that says which case you hit.

Scopes

Scopes follow the resource:action shape, like GitHub. There is no implicit hierarchy — trips:write does not grant trips:read. Add both if you need both.

ScopeGrants
trips:readRead trips, days, segments.
trips:writeCreate and update trips.
bookings:readRead bookings and their snapshots.
bookings:write(Reserved — booking writes go through the storefront today.)
clients:readRead CRM contacts.
clients:writeCreate and update CRM contacts.
payments:readRead booking payment ledger entries.
libraries:readRead the hotels, activities, vehicles, guides catalogs.
webhooks:readList webhook subscriptions and recent deliveries.
webhooks:writeCreate, delete, and test webhook subscriptions.

A request whose token doesn't carry the required scope returns 403 Forbidden, type .../errors/scope-required, with a required_scope field naming the scope that is missing.

Storing the token

A handful of patterns that work; one anti-pattern that doesn't:

WhereVerdict
Vault, 1Password, AWS/GCP Secrets✓ Read at process start; never write to disk.
GitHub Actions encrypted secrets✓ Audit who can view; rotate on contractor offboarding.
.env.local on a dev machine✓ For dev tokens (tk_test_…). Add .env* to .gitignore.
Server-side cookie✓ If your backend proxies API calls for a SPA. HttpOnly + Secure.
Browser localStorage or JS bundle✗ Never for an integration token. Treat any token shipped to a browser as compromised.
Plain-text on a shared spreadsheet✗ Never. Rotate immediately if this happened.

The token is the credential. Anyone who can read it can act as your tenant inside its scopes.

The API Explorer on this site is the one deliberate exception, and it is not a pattern to copy: you paste a token into your own browser to fire a call by hand. It keeps the token in sessionStorage — this tab only — and offers localStorage as an explicit opt-in. Use a tk_test_… token there where you can, and clear it when you are done.

Plan tiers

API access is gated by your subscription tier. On a plan without API access, every /v1/* request returns 403 Forbidden with type .../errors/plan-required, a required_plans array listing the plans that do include it, and current_plan showing where you are today.

Rotation

To rotate:

  1. Create the new token with the same scopes.
  2. Roll it out — deploy the new value to your integration's secrets store.
  3. Verify in your logs that traffic is going through the new token.
  4. Revoke the old one from the dashboard. Revocation takes effect immediately (the row is revokedAt-soft-deleted; audit log entries that reference its id still resolve).

There is no overlap window beyond what your deployment takes. Tokens are cheap to mint.

Leak response

If a token may have leaked (committed to a public repo, posted in chat, included in a customer email):

  1. Revoke it now from the operator dashboard. The next call on the leaked token fails with 401 and type .../errors/unauthorized (detail: "Token has been revoked.").
  2. Check lastUsedAt and lastUsedIp on the dashboard's token row. If activity happened from an unexpected IP after the suspected leak time, escalate to your incident channel.
  3. Mint a new token with the same scopes.
  4. Audit what the leaked token could do with its scope set; e.g. a trips:read leak is informational, a clients:write leak warrants checking for unexpected CRM rows since the leak window.
  5. Rotate the source that leaked (purge git history, regenerate API consumer secrets on your side, etc.).

The Tourical token format is grep-friendly — tk_(live|test)_[a-z0-9]{26} matches any token. Add that pattern to your CI's secret scanner if you don't already.

Why not OAuth?

Bearer tokens cover the only auth pattern we currently support: server-to-server integration on behalf of one tenant. If you need delegated auth on behalf of a Tourical user (e.g. you're building a third-party app that several different operators install), tell us — we're tracking that path but it isn't shipped.

On this page