Skip to content

Access Tokens

Access tokens are short-lived credentials that grant a script or integration the right to read or write to a specific site's REST API. They are entirely separate from your personal ECMWF password and can be revoked at any time.


Why use access tokens?

  • Automation: Scripts and pipelines can authenticate without storing your personal credentials.
  • Scoped access: Tokens can be read-only (Read) or read-write (ReadWrite).
  • Auditable: Each token has an identifier label, so you can track which script used which token.
  • Time-limited: Tokens expire after the duration you choose, reducing exposure if one leaks.

Concepts

Concept Meaning
Identifier A free-text label you choose, used for audit purposes (e.g., 'backup-script', 'ci-pipeline')
Duration How long the token is valid, in seconds
Permission Read (download only) or ReadWrite (upload, download, delete)
Token hash A SHA256 fingerprint of the token. Use this to revoke or delete history entries without exposing the token string itself

SiteAccessPermission values:

from sites.sdk.sites.utils import SiteAccessPermission

SiteAccessPermission.Read       # read-only access to the site
SiteAccessPermission.ReadWrite  # read and write access

Getting a content manager

All token operations are performed through a SiteContentManagement instance. You need to authenticate as someone with administration rights over the site.

from sites.sdk.sites import Site, Authenticator

site = Site.from_space_and_name(space='docs', name='my-project')

# Use the site's unique bearer token OR hub credentials with admin rights
content_manager = site.get_content_manager(
    authenticator=Authenticator.from_token(token='<admin_site_bearer_token>')
)

Generating a token

from sites.sdk.sites.utils import SiteAccessPermission

token_data = content_manager.generate_token(
    identifier='my-backup-script',      # label for audit trail
    duration=86400,                     # valid for 1 day (86400 seconds)
    permission=SiteAccessPermission.Read
)

print(token_data['data']['token'])
# abc123def456...

# Save this token and use it with Authenticator.from_token()

Common duration values:

Duration (seconds) Human-readable
3600 1 hour
86400 1 day
604800 7 days
2592000 30 days

Using a generated token

Pass the token to Authenticator.from_token to authenticate as the site:

from sites.sdk.sites import Authenticator

site_auth = Authenticator.from_token(token='abc123def456...')
worker_content_manager = site.get_content_manager(authenticator=site_auth)

# This content manager now uses the scoped token, not your personal credentials
worker_content_manager.download(remote_path='data.csv')

Decoding / inspecting a token

To check what a token contains (identifier, permission, and expiry) without revoking it:

token_info = content_manager.decode_token(token='abc123def456...')
print(token_info)
# {'data': {'identifier': 'my-backup-script', 'permission': 'r', 'expiry': '2026-03-17T10:00:00'}, 'status': 'OK'}

Revoking a token

Revoking a token immediately invalidates it. Provide either the token string or its SHA256 hash — not both.

Revoke by token string:

result = content_manager.revoke_token(token='abc123def456...')
print(result['message'])

Revoke by hash (preferred — avoids passing the raw token around):

result = content_manager.revoke_token(token_hash='e3b0c44298fc1c149afbf...')
print(result['message'])

Viewing token history

Token history records when each identifier+permission pair was most recently used. This is useful for auditing which scripts are actively accessing your site.

List all token history

history = content_manager.token_history()
for entry in history.get('data', []):
    print(entry)

Filter token history

from sites.sdk.sites.utils import SiteAccessPermission

# All entries for a specific identifier
history = content_manager.token_history(identifier='my-backup-script')

# Only read-write tokens
history = content_manager.token_history(permission=SiteAccessPermission.ReadWrite)

# Tokens expiring within the next 7 days
history = content_manager.token_history(expiring_within=604800)

# Include already-expired tokens (they are hidden by default)
history = content_manager.token_history(include_expired=True)

Combine filters freely:

history = content_manager.token_history(
    identifier='ci-pipeline',
    permission=SiteAccessPermission.ReadWrite,
    include_expired=False
)

Deleting a token history entry

Deletes a single history row identified by its SHA256 hash. This does not revoke the token itself — it only removes the history record.

result = content_manager.delete_token_history(
    token_hash='e3b0c44298fc1c149afbf4c8996fb924...'
)
print(result['message'])

Backing up token history

Creates a gzipped JSON backup of all token history entries stored on the server. The backup is kept server-side.

backup = content_manager.backup_token_history()
print(backup['data'])
# {'timestamp': '2026-03-16T15:30:00', 'row_count': 42, 'checksum': 'sha256...'}

Restoring token history

Restores the token history from the latest backup. This is a destructive operation — all current history entries are deleted and replaced with the backup data.

result = content_manager.restore_token_history()
print(result)

Warning: Restoring replaces the entire current history. Use with caution.


End-to-end example: token lifecycle

from sites.sdk.sites import Site, Authenticator
from sites.sdk.sites.utils import SiteAccessPermission

site = Site.from_space_and_name(space='docs', name='my-project')
admin_auth = Authenticator.from_token(token='<admin_site_bearer_token>')
mgr = site.get_content_manager(authenticator=admin_auth)

# 1. Generate a read-only token for a pipeline
token_data = mgr.generate_token(
    identifier='ci-pipeline',
    duration=2592000,             # 30 days
    permission=SiteAccessPermission.Read
)
token = token_data['data']['token']
print('Token:', token)

# 2. Inspect it
info = mgr.decode_token(token=token)
print('Expires:', info['data']['expiry'])

# 3. The pipeline uses it to download data (e.g., a build artifact)
pipeline_auth = Authenticator.from_token(token=token)
pipeline_mgr = site.get_content_manager(authenticator=pipeline_auth)
content = pipeline_mgr.download(remote_path='artefacts/build.zip')

# 4. After the pipeline run, audit who accessed what
history = mgr.token_history(identifier='ci-pipeline')
print(history)

# 5. When no longer needed, revoke it
mgr.revoke_token(token=token)

What's next?