API Tokens (in-app)
Tenantx supports Laravel Sanctum personal access tokens for integrations and automation. In the database they are tied to a specific user account, not to the organization as a separate resource.
What They Are For
- Authenticate non-browser clients to the Tenantx API (
Authorization: Bearer <token>). - Power server-to-server integrations (CRM sync, ETL jobs, internal tools, CI scripts).
- Each request authenticated with a token runs as the user who created the token (see User scope below).
User scope
Understanding “user scope” is what makes tokens safe to reason about.
Token ownership (whose tokens are these?)
- Tokens are created with
$request->user()->createToken(...)and stored on that user’s Sanctum token relation. GET /api/api-tokensreturns only your tokens (the authenticated user).DELETE /api/api-tokens/{id}revokes only a token that belongs to you. You cannot list or revoke another user’s tokens through this API.
So: one user’s tokens never appear in another user’s list, even in the same organization.
Who you are when you call the API with a token
When you send Authorization: Bearer <token>:
- Sanctum resolves the token to a User record.
- Tenant routes still run
auth:sanctumandorganizationmiddleware: the user’s profile suppliesorganization_id, and Spatie permissions use team = that organization. - Authorization for each endpoint is unchanged: controllers check permissions for that user (e.g.
users.read,organizations.update). If the user would get 403 in the browser, the same user with a token gets 403 on that route.
Implication: the token does not grant “org admin by default”. It grants whatever that user’s roles and permissions already allow.
Abilities field (Sanctum)
The kernel creates tokens with Sanctum abilities ['*'] (see ApiTokenController::store). Fine-grained route-level enforcement in Tenantx is still done via Spatie permissions on the resolved user, not via per-route Sanctum ability checks for every endpoint.
Managing tokens vs using tokens
| Action | Requirement |
|---|---|
| Create / list / revoke tokens | User must have api_keys.create / api_keys.read / api_keys.delete (org-scoped). |
| Call other APIs with the token | User must have the permissions those routes require (e.g. profiles.read). |
A user can have api_keys.create and create a token, but that token still cannot access admin-only routes unless that same user also has those permissions.
Workspace context (Bearer requests)
Many tenant routes use workspace.context middleware: the server injects current_workspace_id from the request (not from a header named X-Workspace-*). The SPA passes workspace_id as a query or body parameter when the user may switch workspaces (workspaces_access_all).
Same rules apply when using a Bearer token:
- For routes under
workspace.context, ensure the client sends a validworkspace_idfor that user when required (same as the browser client). - Users without org-wide workspace switching typically use their default workspace as implied by the backend for their profile.
If you omit workspace_id where the middleware expects it, you may get 403 even though the token is valid.
Practical patterns
-
Dedicated integration user
Create a user in the org (e.g.integrations@yourcompany.com), assign a minimal role with only the permissions the integration needs, plusapi_keys.*if that user should rotate its own tokens. Create tokens while logged in as that user (or via a one-time setup flow). -
Human operator
An org admin creates a token for their own account only if they accept that any holder of the token can do everything they can do in the API (subject to permissions). Prefer short-lived storage and revoke when done. -
Never share one token across people
Revocation and auditing are per token row; sharing breaks accountability.
Permissions (to use the token management UI/API)
api_keys.readapi_keys.createapi_keys.delete
By default, these are intended for organization-level administrators, not workspace-scoped admin (see PermissionSeeder::isSchoolAdminRestrictedPermission).
UI
- Primary page:
/org-admin/api-keys - Legacy URL:
/settings/api-keys(redirects to org-admin route when permitted)
Token management routes live under workspace.context in api.php; open the page with a normal workspace context so the client sends workspace_id as for other settings.
Flow:
- Enter token name.
- Click create.
- Copy token immediately (raw token is shown once).
- Store it in your secret manager.
- Revoke when no longer needed.
API Endpoints (token management)
GET /api/api-tokens— list your token metadata (no raw secret values).POST /api/api-tokens— body{ "name": "..." }; returns the plain token once in the JSON response.DELETE /api/api-tokens/{id}— revoke your token by id.
All require auth:sanctum, organization, and the corresponding api_keys.* permission, plus valid workspace context where the route group applies.
Example: call the API as that user
curl -H "Authorization: Bearer <TOKEN>" \
-H "Accept: application/json" \
"https://yourdomain.com/api/permissions/user?workspace_id=<WORKSPACE_UUID>"
Use the same base URL and path conventions as the SPA. Add workspace_id on routes that expect workspace context when your user is allowed to switch workspaces.
CRM usage clarification (important)
A CRM or any external app can use API tokens.
- A token can call all API routes that the token owner user is authorized to call.
- A token cannot bypass organization boundaries, permission checks, or workspace-context requirements.
So if your CRM needs “all data” for an organization, create a dedicated integration user with the required broad read permissions (or organization-admin-level access if truly needed), then create the token from that user.
Quick checklist for CRM integrations
- Create an
integrations@...user in the target organization. - Assign least-privilege permissions needed by the CRM.
- Create an API token while signed in as that user.
- Use
Authorization: Bearer <token>on CRM requests. - Include
workspace_idon workspace-scoped endpoints where applicable. - Rotate/revoke token regularly and when ownership changes.
Security Guidelines
- Treat tokens like passwords.
- Never commit tokens to source control.
- Prefer one token per integration and one integration user with least privilege.
- Rotate/revoke on staff or infrastructure changes.
- Remember: token = that user for org data access and permission checks.