Workspace Admin API
The Workspace Admin API lets server-to-server integrations administer one EVCharge workspace. This documentation is for workspace administrators and external API integrators. It uses revocable, workspace-bound access tokens and fine-grained scopes; customer login tokens and browser sessions are not accepted.
This directory is the canonical developer reference for the /v2/admin API implemented by app-api. It does not document customer/mobile routes or the admin portal's own HTTP routes.
- Endpoint requests and responses lists all 87 registered endpoints, scopes, parameters, request bodies, and success bodies.
- Schemas and examples defines the reusable request/response types and representative JSON payloads.
- Response and error contract defines every shared and endpoint-specific non-success body.
Base URL and resource boundary
All routes are relative to the deployed app-api origin:
https://api.evcharge.mobi/v2/admin/...
The workspace ID is not accepted in the URL, query string, or request body. Each access token is bound to exactly one workspace, and the API derives cpoId from the authenticated token principal. If a nested site, charger, user, tariff, profile, assignment, or transaction does not belong to that workspace, the API returns the same non-enumerating 404 RESOURCE_NOT_FOUND response.
Getting an access token
A human workspace administrator creates tokens in Workspace Settings under API access. Token management is intentionally not exposed to workspace tokens.
- The administrator must have workspace-edit permission and the
apiAccessentitlement. - The workspace's host must enable the Admin API through
adminApiEnabled; otherwise a valid token receives a non-enumerating404fromapp-api. - The administrator can grant only scopes allowed by their own current permissions and workspace entitlements.
- New tokens may be non-expiring (the Workspace Settings default) or use an administrator-selected whole-day lifetime from 1 to 365 days; the server computes finite absolute expiries. Non-expiring tokens remain valid until revoked.
- A workspace can have at most 50 active tokens.
- The plaintext credential is shown once. Store it immediately in a secret manager.
- Name, scopes, and expiry are immutable. To rotate, create a replacement, deploy it, verify it, and revoke the old token.
- Revocation is immediate; revoked rows remain visible as immutable, redacted workspace history, and there is no bearer-token introspection or hard-delete endpoint.
app-api has no endpoint that issues, refreshes, lists, or revokes these credentials. Obtaining the token is therefore a prerequisite performed through the human admin portal, not another endpoint in this reference.
Never put the token in source control, URLs, query strings, cookies, logs, analytics, or error reports.
Authentication
Send exactly one RFC 6750-style header on every request:
Authorization: Bearer evc_ws_<selector>.<secret>
The exact opaque credential is evc_ws_ followed by a 16-character canonical base64url selector, ., and a 43-character canonical base64url secret. Send the complete credential verbatim; do not base64-encode it again.
The API rejects duplicate authorization headers, comma-joined values, other schemes, empty bearer values, customer JWTs, and credentials supplied anywhere except this header.
Malformed, unknown, expired, revoked, or otherwise invalid tokens all receive the same response:
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer
Cache-Control: no-store
Content-Type: application/json
{
"error": "Unauthorized",
"code": "INVALID_WORKSPACE_TOKEN"
}
The service checks token status and current workspace entitlements on every request. Removing an entitlement can therefore disable affected scopes before the token expires.
Workspace tokens are opaque credentials, not JWTs. They have no refresh token and there is no refresh endpoint. A finite token stops authenticating at its absolute expiresAt; a non-expiring token remains valid until revoked. Rotation means creating a replacement credential in the portal, deploying it to the client, and revoking the old credential.
Request conventions
- Use
Content-Type: application/jsonfor requests with a JSON body. - Optional
X-Request-Idrequest values are trimmed and accepted only when they contain 1-128 characters fromA-Z,a-z,0-9,.,_,:, or-; otherwise the server generates a UUID. Every response returns the selected value asX-Request-Id. - Optional
X-Error-Format: envelopeconverts every JSON error response to the structured envelope documented in Response and error contract. Any other value preserves the default compatibility body. - IDs in path parameters are positive integers. Access-token IDs returned by the portal are decimal strings because they are unsigned 64-bit database values.
- Date-time values are RFC 3339 strings with an explicit offset, for example
2026-08-13T12:30:00Z. - Successful deletes normally return
204 No Content. - Create operations normally return
201 Created. - OCPP work that has only been queued may return
202 Accepted; do not interpret that as confirmation from the charger unless the response explicitly says so. - Authenticated responses use
Cache-Control: no-store. QR and report-export endpoints return binary bodies with explicit content headers. - Allowed browser origins can read
X-Request-Id,Idempotency-Replayed,Retry-After, andContent-Dispositionthrough CORS. This lets browser clients correlate requests, detect idempotent replays, honor retry timing, and retain server-selected download filenames.
Idempotency
All POST, PUT, PATCH, and DELETE admin requests must include one Idempotency-Key header. Keys contain 1-255 visible characters and are scoped to the token, method, and path.
Idempotency-Key: provisioning-2026-08-13-site-42
Reusing a completed key with the identical URL and body replays the stored response and adds:
Idempotency-Replayed: true
Reusing a key with a different request returns 409 IDEMPOTENCY_CONFLICT. A request still being processed returns 409 IDEMPOTENT_REQUEST_PROCESSING with Retry-After: 5.
Completed responses and terminal outcomes are retained for replay or reconciliation for seven days after their last update. After that retention period, the same key may be treated as a new command, so clients must use globally unique keys and must not intentionally recycle them.
If a processing lease expires, the connection closes after execution starts, or the operation returns a server error, the API cannot safely prove whether an external effect occurred. During the seven-day retention window, the key is terminally indeterminate and returns 409 IDEMPOTENCY_OUTCOME_UNKNOWN; it is not reclaimed or automatically executed again. Verify the operation's effects before taking manual recovery action, and do not submit the same operation under a new key until that reconciliation is complete.
All mutating admin endpoints, including POST /reports/send, require Idempotency-Key.
Rate limits
Limits are applied to both the individual token and its workspace. The committed defaults are:
| Limit | Default |
|---|---|
| Window | 60 seconds |
| Per token | 300 requests |
| Per workspace | 1,000 requests |
A limited request returns 429 Too Many Requests and a Retry-After header. Deployments can override these values.
Errors
JSON errors use a stable code where the caller needs to branch:
{
"error": "Insufficient workspace-token scope",
"code": "INSUFFICIENT_SCOPE"
}
Validation errors use field arrays:
{
"validationErrors": {
"name": ["String must contain at least 1 character(s)"]
}
}
Common statuses are:
| Status | Meaning |
|---|---|
400 | Invalid idempotency/user cursor/geocoder contract; path/body/query schema failures are normally 422 |
401 | Invalid workspace token |
403 | Valid token without the required effective scope or entitlement |
404 | Workspace or nested resource is not visible to this token |
409 | Resource state, dependency, concurrency, or idempotency conflict |
422 | Field validation, bounded-range, or export-limit failure |
429 | Token or workspace rate limit exceeded |
502 | A downstream operation such as report delivery failed |
503 | Authorization or idempotency storage is temporarily unavailable |
Scope catalog
The following scope catalog is used when an administrator creates a token. A write or high-impact scope may imply the corresponding read scope, but read scopes never grant writes. Available scopes depend on the administrator's permissions and the workspace's enabled features.
| Domain | Read scopes | Write and high-impact scopes | Additional entitlement when applicable |
|---|---|---|---|
| Workspace | workspaces:read | workspaces:update | — |
| Sites | sites:read | sites:create, sites:update, sites:delete, sites:power-management | dlmPowerManagement for power management |
| Chargers | chargers:read | chargers:create, chargers:update, chargers:delete, chargers:credentials, chargers:configuration, chargers:firmware, chargers:commands, chargers:public, chargers:connector-settings, chargers:local-users, chargers:tariff-assignments, chargers:profile-assignments | firmwareOperations, publicCharging, tariffs, or smartCharging for the matching capability |
| Users | users:read | users:create, users:update, users:delete, users:invitations, users:rfid, users:access, users:limits | — |
| Charging profiles | chargingProfiles:read | chargingProfiles:create, chargingProfiles:update, chargingProfiles:delete, chargingProfiles:assign | smartCharging |
| Tariffs | tariffs:read | tariffs:create, tariffs:update, tariffs:delete, tariffs:components, tariffs:assign | tariffs |
| Reports | reports:read | reports:export, reports:schedules, reports:send | scheduledReports for schedules and send-now |
Every token also requires the workspace's apiAccess entitlement.
Endpoint reference
The tables below list all 87 workspace-admin routes. Multiple scopes in a cell are all required.
Workspace
| Method | Path | Required scope | Purpose |
|---|---|---|---|
GET | /v2/admin | workspaces:read | Read safe workspace metadata and lookups. |
PATCH | /v2/admin | workspaces:update | Update one or more workspace metadata fields. |
GET | /v2/admin/dashboard | workspaces:read + reports:read | Read bounded workspace counts and 90-day operational aggregates. |
Workspace patch fields: name, email, website, addressLine1, addressLine2, postalCode, city, state, and countryId. At least one field is required. Empty nullable strings normalize to null.
Sites and power management
| Method | Path | Required scope | Purpose |
|---|---|---|---|
GET | /v2/admin/sites | sites:read | List sites with bounded 90-day summaries and site lookups. |
GET | /v2/admin/sites/geocode | sites:read | Search normalized site-address candidates through the shared throttled and cached geocoder. |
POST | /v2/admin/sites | sites:create | Create a site. |
GET | /v2/admin/sites/:siteId | sites:read + reports:read | Read site details, recent sessions, totals, and operational aggregates. |
PATCH | /v2/admin/sites/:siteId | sites:update | Replace the editable site document. |
DELETE | /v2/admin/sites/:siteId | sites:delete | Delete an empty site; dependencies return a conflict. |
GET | /v2/admin/sites/:siteId/power-management | sites:power-management | Read the site's dynamic load-management tree. |
PUT | /v2/admin/sites/:siteId/power-management | sites:power-management | Atomically replace the site's power-management configuration. |
Site create and patch bodies use:
{
"name": "Main depot",
"description": null,
"addressLine1": "1 Example Street",
"addressLine2": null,
"postalCode": "LV-1001",
"city": "Riga",
"state": null,
"countryId": 1,
"timezoneId": 1,
"latitude": 56.9496,
"longitude": 24.1052
}
Site geocoding accepts optional street, city, state, country, and two-letter countryCode query fields plus the Accept-Language request header. The combined street/city/state search must contain at least two characters. Responses contain only placeId, displayName, latitude, and longitude; provider credentials and provider-specific metadata are not exposed. The server applies the shared one-request-per-second provider queue, eight-second timeout, bounded 24-hour cache, and global workspace-token rate limits.
Power management accepts { "pools": [...] }. A pool has id, name, limit, optional meterSources, and recursive children. A charger child has chargepointId and priority. The API caps the tree and meter counts and performs two-phase OCPP clear/revalidation when replacing installed state. A refused, timed-out, or raced change returns 409 POWER_MANAGEMENT_CONFLICT while preserving the previous configuration.
Chargers
| Method | Path | Required scope | Purpose or input |
|---|---|---|---|
GET | /v2/admin/chargers | chargers:read | List chargers with status and bounded energy/cost summaries. |
POST | /v2/admin/chargers | chargers:create | Create from name, identifier, optional password, and siteId. |
GET | /v2/admin/chargers/:chargerId | chargers:read | Read charger, EVSE, connector, lookup, and status data. |
PATCH | /v2/admin/chargers/:chargerId | chargers:update | Update name, siteId, invertedRfid, smartMode, or automationDisabled. |
DELETE | /v2/admin/chargers/:chargerId | chargers:delete | Delete a charger subject to dependency and billing rules. |
PATCH | /v2/admin/chargers/:chargerId/credentials | chargers:credentials | Replace the charger password with { "password": "..." }. |
PATCH | /v2/admin/chargers/:chargerId/connectors/:portId | chargers:connector-settings | Update connector format/type, power type, voltage, amperage, and power limits. |
PATCH | /v2/admin/chargers/:chargerId/public-listing | chargers:public | Set `{ "isPublic": true |
GET | /v2/admin/chargers/:chargerId/public-readiness | chargers:read | Read public-charging readiness and missing requirements. |
GET | /v2/admin/chargers/:chargerId/logs | chargers:read | Read cursor-paginated, redacted OCPP logs. |
GET | /v2/admin/chargers/:chargerId/sessions | chargers:read + reports:read | Read paginated charging sessions and totals. |
GET | /v2/admin/chargers/:chargerId/qr | chargers:read | Render a 300-pixel PNG QR; optional connector=0..255. |
GET | /v2/admin/chargers/:chargerId/composite-schedules | chargers:read | Read persisted composite-schedule history. |
GET | /v2/admin/chargers/:chargerId/configuration | chargers:read | Read OCPP configuration with sensitive values redacted. |
PATCH | /v2/admin/chargers/:chargerId/configuration | chargers:configuration | Send a configuration change with key and value. |
POST | /v2/admin/chargers/:chargerId/configuration/reload | chargers:configuration | Request a fresh charger configuration. |
GET | /v2/admin/chargers/:chargerId/firmware | chargers:read | Read current firmware and update status. |
POST | /v2/admin/chargers/:chargerId/firmware | chargers:firmware | Queue an update from an HTTP(S) { "url": "..." }. |
POST | /v2/admin/chargers/:chargerId/commands/reset | chargers:commands | Send `{ "resetType": "Soft" |
POST | /v2/admin/chargers/:chargerId/commands/availability | chargers:commands | Send `{ "availabilityType": "Operative" |
POST | /v2/admin/chargers/:chargerId/commands/trigger | chargers:commands | Trigger BootNotification, Heartbeat, StatusNotification, or MeterValues. |
POST | /v2/admin/chargers/:chargerId/commands/unlock | chargers:commands | Unlock a positive connectorId. |
POST | /v2/admin/chargers/:chargerId/commands/composite-schedule | chargers:commands | Request a composite schedule for connectorId, durationSeconds, and chargingRateUnit. |
Charger log query parameters are cursor, limit (default 50, maximum 200), from, to, action, direction, and levelId. Session parameters are page, limit (default 25, maximum 100), from, to, and customerId (a positive ID or unknown). Composite-schedule history uses cursor, limit (default 20, maximum 50), and optional connectorId.
Reports
| Method | Path | Required scope | Purpose |
|---|---|---|---|
GET | /v2/admin/reports/data | reports:read | Read filtered, cursor-paginated charging-session report data. |
GET | /v2/admin/reports/measurements | reports:read | Read bounded meter values for an owned transaction. |
GET | /v2/admin/reports/export | reports:export | Download a CSV or PDF export. |
GET | /v2/admin/reports/schedule | reports:read | Read the workspace-owned export schedule, or null. |
PUT | /v2/admin/reports/schedule | reports:schedules | Create or replace the workspace-owned export schedule. |
POST | /v2/admin/reports/send | reports:send | Email the previous UTC calendar month's saved export; idempotency key required. |
Report data and export filters are from, to, siteId, chargerId, userId, anonymous, and active. from/to default to the latest 90-day window and may not span more than 90 days. userId cannot be combined with anonymous=true.
- Data pagination: opaque
cursor, defaultlimit=50, maximum 100. - Measurements: required
transactionLogId, opaquecursor, defaultlimit=500, maximum 1,000. - Export:
format=csv|pdf, defaultcsv, hard limit 2,000 rows. - Export responses use
Content-Disposition: attachment,Cache-Control: private, no-store, andX-Content-Type-Options: nosniff.
Schedule body:
{
"enabled": true,
"recipientEmail": "reports@example.com",
"format": "csv",
"sendDay": 5,
"sendTime": "08:30"
}
sendDay is 1-28 and sendTime is interpreted in UTC. The scheduler catches up after a missed scheduled minute during the current delivery month. Failed pre-handoff builds retry after 5 and 10 minutes, an interrupted building claim is reclaimable after 30 minutes, and automatic delivery stops after three attempts. The scheduler persists indeterminate immediately before SMTP handoff; both indeterminate and the legacy sending status are terminal because automatically retrying either could send a duplicate attachment. Operators must reconcile those outcomes manually before changing the schedule state. Each tick selects at most ten schedules, stops starting work after a 45-second budget, and uses bounded SMTP timeouts. The job is disabled by default and must be explicitly enabled after SMTP and capacity validation. The API schedule is owned by the workspace rather than borrowing the identity of the human who created the token.
Tariffs
| Method | Path | Required scope | Purpose |
|---|---|---|---|
GET | /v2/admin/tariffs | tariffs:read | List tariffs and lookups. |
POST | /v2/admin/tariffs | tariffs:create | Create a tariff. |
GET | /v2/admin/tariffs/:tariffId | tariffs:read | Read one owned tariff and its components. |
PATCH | /v2/admin/tariffs/:tariffId | tariffs:update | Update the tariff definition. |
PUT | /v2/admin/tariffs/:tariffId/components | tariffs:components | Atomically replace up to 256 components. |
DELETE | /v2/admin/tariffs/:tariffId | tariffs:delete | Delete an unused tariff. |
GET | /v2/admin/tariffs/:tariffId/market-prices | tariffs:read | Read prices for the tariff's owned bidding zone from numeric startsAt. |
GET | /v2/admin/chargers/:chargerId/tariff-assignments | chargers:tariff-assignments + tariffs:read | List charger tariff assignments. |
POST | /v2/admin/chargers/:chargerId/tariff-assignments | chargers:tariff-assignments + tariffs:assign | Assign a tariff. |
PATCH | /v2/admin/chargers/:chargerId/tariff-assignments/:assignmentId | chargers:tariff-assignments + tariffs:assign | Replace an assignment target. |
DELETE | /v2/admin/chargers/:chargerId/tariff-assignments/:assignmentId | chargers:tariff-assignments + tariffs:assign | Remove an assignment. |
A tariff body contains name, exactly one of currencyId, currencyCode, or currency, optional biddingZoneId, and tax. An assignment body is:
{ "tariffId": 17, "connectorId": 0 }
For assignment creation, an omitted connectorId or 0 applies the tariff to every physical connector. Assignment updates target one persisted assignment and therefore require an explicit positive physical connectorId; 0 is rejected.
A component contains typeId, seasonId, optional priority, price, and optional month, weekday, and hour validity bounds.
Charging profiles
| Method | Path | Required scope | Purpose |
|---|---|---|---|
GET | /v2/admin/charging-profiles | chargingProfiles:read | List profiles and owned lookups. |
POST | /v2/admin/charging-profiles | chargingProfiles:create | Create a profile with an optional schedule. |
GET | /v2/admin/charging-profiles/:profileId | chargingProfiles:read | Read one owned profile. |
PATCH | /v2/admin/charging-profiles/:profileId | chargingProfiles:update | Replace profile metadata. |
PUT | /v2/admin/charging-profiles/:profileId/schedule | chargingProfiles:update | Replace the schedule with 1-256 periods. |
DELETE | /v2/admin/charging-profiles/:profileId | chargingProfiles:delete | Clear installed assignments and delete only after accepted OCPP results and revalidation. |
GET | /v2/admin/chargers/:chargerId/profile-assignments | chargers:profile-assignments + chargingProfiles:read | List charger profile assignments. |
POST | /v2/admin/chargers/:chargerId/profile-assignments | chargers:profile-assignments + chargingProfiles:assign | Assign and synchronize a profile. |
PATCH | /v2/admin/chargers/:chargerId/profile-assignments/:assignmentId | chargers:profile-assignments + chargingProfiles:assign | Replace and synchronize an assignment. |
DELETE | /v2/admin/chargers/:chargerId/profile-assignments/:assignmentId | chargers:profile-assignments + chargingProfiles:assign | Clear and remove an assignment. |
Profile metadata fields are label, locationId, stackLevel, purposeId, kindId, optional recurrencyKindId, validFrom, validTo, startSchedule, rateUnitId, and optional minChargingRate. A schedule period contains startPeriod, limit, and optional numberOfPhases. An assignment body contains profileId and connectorId (default 0).
Profile deletes and power-management replacement may wait for charger acknowledgement. A rejection, timeout, or concurrent state change returns a conflict and retains the database configuration rather than pretending the charger was cleared.
Users, invitations, RFID, access, and limits
| Method | Path | Required scope | Purpose |
|---|---|---|---|
GET | /v2/admin/users | users:read | List members and invitations. |
POST | /v2/admin/users | users:create | Attach an already registered customer by email and workspace name. |
POST | /v2/admin/users/invitations | users:invitations + users:access; also users:rfid when RFID is supplied | Invite an unknown email or attach an existing customer. |
POST | /v2/admin/users/invitations/resend | users:invitations | Resend using { "email": "..." }, subject to recipient cooldown. |
DELETE | /v2/admin/users/invitations | users:invitations | Cancel an invitation; email stays in the JSON body. |
GET | /v2/admin/users/:userId | users:read + reports:read | Read user details and bounded session totals. |
PATCH | /v2/admin/users/:userId | users:update | Set the workspace-specific display name. |
DELETE | /v2/admin/users/:userId | users:delete | Remove the member and related workspace access subject to conflicts. |
GET | /v2/admin/users/:userId/vehicles | users:read | List owned vehicles. |
GET | /v2/admin/users/:userId/rfids | users:read | List RFID metadata with masked UIDs. |
POST | /v2/admin/users/:userId/rfids | users:rfid | Add uid and optional name; responses expose only the masked UID. |
PATCH | /v2/admin/users/:userId/rfids/:rfidId | users:rfid | Update the RFID label. |
DELETE | /v2/admin/users/:userId/rfids/:rfidId | users:rfid | Delete an RFID and synchronize affected chargers. |
GET | /v2/admin/users/:userId/access | users:read | Read charger-access grants. |
PUT | /v2/admin/users/:userId/access | users:access | Replace access using allChargers and unique chargerIds. |
DELETE | /v2/admin/users/:userId/access/:accessId | users:access | Revoke one access row. |
GET | /v2/admin/users/:userId/limits | users:read | List charging limits. |
POST | /v2/admin/users/:userId/limits | users:limits | Create limitTypeId, energyValue, and/or timeValue. |
PATCH | /v2/admin/users/:userId/limits/:limitId | users:limits | Update one or more limit fields. |
DELETE | /v2/admin/users/:userId/limits/:limitId | users:limits | Delete a limit. |
GET | /v2/admin/chargers/:chargerId/local-users | chargers:read + users:read | List charger-local users and pending invitations. |
POST | /v2/admin/chargers/:chargerId/local-users | chargers:local-users + users:access | Add an existing customerId with optional expiresAt. |
POST | /v2/admin/chargers/:chargerId/local-users/invitations | chargers:local-users + users:invitations + users:access | Add or invite by email. |
DELETE | /v2/admin/chargers/:chargerId/local-users/invitations | chargers:local-users + users:invitations | Cancel a local-user invitation by email. |
PATCH | /v2/admin/chargers/:chargerId/local-users/:localUserId | chargers:local-users + users:access | Set nullable expiresAt. |
DELETE | /v2/admin/chargers/:chargerId/local-users/:localUserId | chargers:local-users + users:access | Remove local access and synchronize OCPP state. |
Vehicle pagination and status=members user pagination use a positive numeric cursor. Invitation pages use an opaque i2.* cursor, while status=all uses an opaque a2.* composite cursor after the first page (a numeric cursor remains valid only as a legacy member-stream continuation). User search is supplied in the X-Admin-User-Search request header so email addresses are not placed in URLs or access logs. limit defaults to 50 and is capped at 100; user listing supports status=all|members|invitations. Treat all returned cursors as opaque and return them unchanged.
Direct user creation does not create a password, login identity, or session. It only attaches an already registered customer. Use the invitation endpoint for an unknown email. The protected Tagless system user cannot be modified through these routes.
Examples
Set values in your shell without printing the token:
export ADMIN_API_BASE_URL="https://api.example.com"
export WORKSPACE_TOKEN="<read from secret manager>"
List sites:
curl --fail-with-body \
-H "Authorization: Bearer ${WORKSPACE_TOKEN}" \
"${ADMIN_API_BASE_URL}/v2/admin/sites"
Create a site safely:
curl --fail-with-body \
-X POST \
-H "Authorization: Bearer ${WORKSPACE_TOKEN}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: site-main-depot-v1" \
--data '{
"name":"Main depot",
"description":null,
"addressLine1":"1 Example Street",
"addressLine2":null,
"postalCode":"LV-1001",
"city":"Riga",
"state":null,
"countryId":1,
"timezoneId":1,
"latitude":56.9496,
"longitude":24.1052
}' \
"${ADMIN_API_BASE_URL}/v2/admin/sites"
Download a CSV report for an explicit UTC range:
curl --fail-with-body \
-H "Authorization: Bearer ${WORKSPACE_TOKEN}" \
-o report.csv \
"${ADMIN_API_BASE_URL}/v2/admin/reports/export?format=csv&from=2026-07-01T00%3A00%3A00Z&to=2026-08-01T00%3A00%3A00Z"
Deliberate exclusions
The workspace-token API does not expose operations that require a human principal or broader authority. These include:
- creating, revealing, rotating, or revoking tokens with another token;
- login, password reset, sessions, human role or privilege delegation, and workspace ownership changes;
- workspace creation/deletion and billing-account membership;
- payment settings, bank data, Stripe onboarding, plans, invoices, and host billing;
- host-wide support operations and provider webhooks;
- human notifications, navigation redirects, branding assets, and development/test routes.