Authentication
The Elastio API authenticates every request with a personal access token (PAT) sent as a bearer token. This page covers how tokens work, how to issue and scope them, how to use them, and how to rotate and revoke them safely.
How tokens work
Section titled “How tokens work”- A personal access token belongs to the user who issued it, in one tenant. The API calls it the requesting user: the list, update and revoke operations only act on that user’s own tokens.
- Only tenant admins can issue and manage tokens. The token endpoints answer
403when the caller is not a tenant admin. - A token has either universal access (
all_scopes: true) or a list of grants, each pairing a scope category withreadorwriteaccess. See Scopes. - A token can have an expiry (
expires_at). Without one, it never expires. - The raw token value is returned once, when the token is issued. Elastio does not show it again.
Security guidelines
Section titled “Security guidelines”Treat a personal access token like a password for your tenant.
- Store tokens in a secret manager, such as AWS Secrets Manager, Azure Key Vault, Google Secret Manager or HashiCorp Vault, or your CI system’s encrypted secrets. Never commit them to source control or paste them into tickets or chat.
- Grant the least privilege that works. Prefer specific grants over
all_scopes, andreadoverwrite. A reporting job that only reads data should not be able to change policies. - Use one token per integration. Separate tokens let you see each integration’s
last_used_atand revoke one without breaking the others. - Set an expiry and rotate. Issue tokens with an
expires_at, and replace them before they expire. Rotate immediately if a token might have been exposed. - Revoke tokens you no longer need, including tokens belonging to integrations you have retired.
- Copy the token when it is issued. It is shown only once. If you lose it, revoke it and issue a new one.
Scopes
Section titled “Scopes”Each grant pairs a scope category with an access level. write implies read.
| Category | read |
write |
|---|---|---|
inventory |
Yes | Yes |
security |
Yes | Yes |
protection |
Yes | Yes |
reporting |
Yes | Yes |
integrations |
Yes | Yes |
admin |
Yes | Yes |
A token can carry grants for several categories, for example read on inventory and write on security. Setting all_scopes to true gives the token universal API access, and grants is ignored.
Issue a token
Section titled “Issue a token”Tenant admins issue tokens in the Elastio console. Your first token has to come from the console; after that you can issue further tokens with the API.
Issue a token with the API
Section titled “Issue a token with the API”The request body (IssuePATRequestBody):
| Field | Type | Description |
|---|---|---|
name |
string | Required. A name that identifies the token, such as the integration that uses it. |
all_scopes |
boolean | Universal API access. When true, grants is ignored. |
grants |
array of ScopeGrant |
Required and non-empty when all_scopes is false. Each item is { "category": "…", "access": "read" | "write" }. |
expires_at |
string (date-time) | Optional RFC 3339 timestamp. Unset means the token never expires. |
Only tenant admins can manage tokens. The gateway derives your role from the token you call with, so no extra headers are needed.
curl -X POST "https://acme.app.elastio.com/gateway/shared-services/v1/personal-access-tokens" \ -H "Authorization: Bearer $ELASTIO_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "name": "nightly-inventory-export", "all_scopes": false, "grants": [ { "category": "inventory", "access": "read" }, { "category": "reporting", "access": "read" } ], "expires_at": "2027-01-31T00:00:00Z" }'import os, requests
BASE = "https://acme.app.elastio.com"HEADERS = {"Authorization": f"Bearer {os.environ['ELASTIO_TOKEN']}"}
body = { "name": "nightly-inventory-export", "all_scopes": False, "grants": [ {"category": "inventory", "access": "read"}, {"category": "reporting", "access": "read"}, ], "expires_at": "2027-01-31T00:00:00Z",}resp = requests.post(f"{BASE}/gateway/shared-services/v1/personal-access-tokens", headers=HEADERS, json=body, timeout=30)resp.raise_for_status()issued = resp.json()new_token = issued["token"] # store this in your secret manager nowprint(issued["details"]["id"])$Base = "https://acme.app.elastio.com"$Headers = @{ Authorization = "Bearer $env:ELASTIO_TOKEN" }$Body = @{ name = "nightly-inventory-export" all_scopes = $false grants = @( @{ category = "inventory"; access = "read" }, @{ category = "reporting"; access = "read" } ) expires_at = "2027-01-31T00:00:00Z"} | ConvertTo-Json -Depth 5
$issued = Invoke-RestMethod -Method Post -Uri "$Base/gateway/shared-services/v1/personal-access-tokens" ` -Headers $Headers -ContentType "application/json" -Body $Body$issued.details.idA successful request returns 201 Created with the raw token in token and its metadata in details (TokenDetails):
{ "token": "…", "details": { "id": "3f8a2c71-5d4e-4b19-a6c0-9e2d7f1b8a53", "name": "nightly-inventory-export", "user_id": "c41e7b20-9f3a-4d6e-8b15-0a2f6c9d3e71", "tenant_id": "5a9d0e3c-2b7f-4c18-9e64-d1f3a8b2c605", "all_scopes": false, "grants": [ { "category": "inventory", "access": "read" }, { "category": "reporting", "access": "read" } ], "created_at": "2026-10-12T09:30:00Z", "updated_at": "2026-10-12T09:30:00Z", "expires_at": "2027-01-31T00:00:00Z" }}Use a token
Section titled “Use a token”Send the token in the Authorization header with the Bearer scheme on every request:
curl "https://acme.app.elastio.com/gateway/api/v2/incidents?status=open" \ -H "Authorization: Bearer $ELASTIO_TOKEN"import os, requests
BASE = "https://acme.app.elastio.com"HEADERS = {"Authorization": f"Bearer {os.environ['ELASTIO_TOKEN']}"}
resp = requests.get(f"{BASE}/gateway/api/v2/incidents", headers=HEADERS, params={"status": "open"}, timeout=30)resp.raise_for_status()$Base = "https://acme.app.elastio.com"$Headers = @{ Authorization = "Bearer $env:ELASTIO_TOKEN" }Invoke-RestMethod -Uri "$Base/gateway/api/v2/incidents?status=open" -Headers $HeadersA missing, malformed, expired or revoked token returns 401. A valid token without access to the operation returns 403. See Errors.
Manage tokens
Section titled “Manage tokens”List tokens
Section titled “List tokens”Returns the active tokens issued by the requesting user, in tokens_details, with a total. Page with limit and offset. Each entry is a TokenDetails object, including last_used_at and expires_at, which tell you which tokens are idle or close to expiry. The raw token value is never returned.
curl "https://acme.app.elastio.com/gateway/shared-services/v1/personal-access-tokens?limit=50&offset=0" \ -H "Authorization: Bearer $ELASTIO_TOKEN"resp = requests.get( f"{BASE}/gateway/shared-services/v1/personal-access-tokens", headers=HEADERS, params={"limit": 50, "offset": 0}, timeout=30,)resp.raise_for_status()for t in resp.json()["tokens_details"]: print(t["id"], t["name"], t.get("last_used_at"), t.get("expires_at"))$list = Invoke-RestMethod -Uri "$Base/gateway/shared-services/v1/personal-access-tokens?limit=50&offset=0" -Headers $Headers$list.tokens_details | Select-Object id, name, last_used_at, expires_atUpdate a token
Section titled “Update a token”/gateway/shared-services/v1/personal-access-tokens/{token_id}Update a personal access tokenChange a token’s name, all_scopes, grants or expires_at. Fields you omit are left alone: omitting both all_scopes and grants leaves permissions unchanged (a rename), and omitting expires_at leaves the expiry unchanged. The response is the updated TokenDetails.
curl -X PATCH "https://acme.app.elastio.com/gateway/shared-services/v1/personal-access-tokens/3f8a2c71-5d4e-4b19-a6c0-9e2d7f1b8a53" \ -H "Authorization: Bearer $ELASTIO_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "grants": [ { "category": "inventory", "access": "read" } ] }'token_id = "3f8a2c71-5d4e-4b19-a6c0-9e2d7f1b8a53"resp = requests.patch( f"{BASE}/gateway/shared-services/v1/personal-access-tokens/{token_id}", headers=HEADERS, json={"grants": [{"category": "inventory", "access": "read"}]}, timeout=30,)resp.raise_for_status()print(resp.json()["grants"])$TokenId = "3f8a2c71-5d4e-4b19-a6c0-9e2d7f1b8a53"$Body = @{ grants = @(@{ category = "inventory"; access = "read" }) } | ConvertTo-Json -Depth 5Invoke-RestMethod -Method Patch -Uri "$Base/gateway/shared-services/v1/personal-access-tokens/$TokenId" ` -Headers $Headers -ContentType "application/json" -Body $BodyRevoke tokens
Section titled “Revoke tokens”Revokes one or more of the requesting user’s tokens. Send their IDs in token_ids. A successful call returns 204 No Content; revoked tokens stop working and return 401.
curl -X POST "https://acme.app.elastio.com/gateway/shared-services/v1/personal-access-tokens/revoke" \ -H "Authorization: Bearer $ELASTIO_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "token_ids": ["3f8a2c71-5d4e-4b19-a6c0-9e2d7f1b8a53"] }'resp = requests.post( f"{BASE}/gateway/shared-services/v1/personal-access-tokens/revoke", headers=HEADERS, json={"token_ids": ["3f8a2c71-5d4e-4b19-a6c0-9e2d7f1b8a53"]}, timeout=30,)resp.raise_for_status() # 204 No Content$Body = @{ token_ids = @("3f8a2c71-5d4e-4b19-a6c0-9e2d7f1b8a53") } | ConvertTo-JsonInvoke-RestMethod -Method Post -Uri "$Base/gateway/shared-services/v1/personal-access-tokens/revoke" ` -Headers $Headers -ContentType "application/json" -Body $BodyList revoked tokens
Section titled “List revoked tokens”/gateway/shared-services/v1/personal-access-tokens/revokedList revoked personal access tokensReturns the requesting user’s revoked tokens in the same shape as the active list (tokens_details and total), paged with limit and offset. Use it to audit what has been revoked.
Expiry and rotation
Section titled “Expiry and rotation”expires_at is an RFC 3339 timestamp. A token with no expires_at never expires, so set one unless you have a reason not to. After the expiry time the token returns 401.
To rotate a token without downtime:
- Issue a new token with the same grants and a new
expires_at. - Store the new token in your secret manager and deploy it to the integration.
- Confirm the integration works, for example by checking that the new token’s
last_used_atis set. - Revoke the old token.
You can also extend a token with PATCH and a later expires_at, but issuing a fresh token limits how long any single secret is valid.
All endpoints
Section titled “All endpoints”Personal Access Tokens
5 operations in the reference →- GET
/gateway/shared-services/v1/personal-access-tokensList personal access tokens - POST
/gateway/shared-services/v1/personal-access-tokensIssue a personal access token - PATCH
/gateway/shared-services/v1/personal-access-tokens/{token_id}Update a personal access token - POST
/gateway/shared-services/v1/personal-access-tokens/revokeRevoke personal access tokens - GET
/gateway/shared-services/v1/personal-access-tokens/revokedList revoked personal access tokens