Docs/Developer/Calling the Atlas API from Your Own Application
DeveloperHow-to

Calling the Atlas API from Your Own Application

The three steps that take a script or service from nothing to its first authenticated Atlas API call: register a client in Pulse, request a token, send it to Atlas.

Updated 4 Nov 2026Timebase Docs

Overview#

Every Atlas API route checks for a bearer token issued by Pulse. Pulse is the identity component of Timebase: it holds the registered clients, checks their credentials and issues the tokens that every other component validates. An application of your own, whether a one-off script, an import pipeline or a service that runs all day, gets onto the Atlas API in three steps:

  1. Register a client in Pulse that is allowed to reach Atlas.
  2. Request a token from Pulse with that client's id and secret.
  3. Call Atlas with the token in the Authorization header.

This page walks the three steps once, end to end, with the smallest working request at each. The articles it draws on go deeper on each part and are linked at the end.


Before you start#

  • A Pulse administrator account. Registering a client is an administrator action.
  • The addresses. Pulse answers on port 4542 and Atlas on 4552 by default. The examples use https://<pulse-host>:4542 and https://<atlas-host>:4552; on a Docker stack these are the service names pulse and atlas from inside the network, and the host's name from outside.
  • Trust. Both addresses serve TLS. If the machine running your application does not trust the certificates, install the issuing root certificate first; see Certificates and trust across Timebase.
  • Authentication is on. It is on by default. If an installation has turned it off, the API is open and none of the steps below are needed, but writes are then attributed to no one; that mode is for local development only.

Step 1: Register a client in Pulse#

A client is an identity Pulse knows about: an id, a secret, the ways it may authenticate, and the components its tokens may reach. Each Timebase component is one; your application needs one of its own. Do not reuse a component's client, such as the seeded Atlas client. Its secret is that component's credential, and sharing it means your application's writes carry Atlas's name.

Sign in to Pulse as an administrator, open Clients and choose Add client:

Field Value Why
Client Id A name for your application, for example ErpImport Becomes the token's subject, and the author of any commit the application makes in Atlas. It cannot be changed later.
Grant Types client_credentials The flow for a service that authenticates with a secret rather than with a signed-in person.
Scopes openid The scope the Timebase components are registered with. The client credentials flow does not depend on it, but keep the registration in the usual shape.
Audiences Atlas The components this client's tokens may reach. Without Atlas here, every call to Atlas is refused with a 401 however valid the token is.

Leave Redirect URIs empty; they belong to interactive sign-in flows.

Save, then open the new client. Its page shows the secret behind Reveal, with a copy control. Copy it into your application's configuration now and keep it as you would any credential. An administrator can reveal it again from the same page, and can rotate it if it is ever exposed; rotating invalidates the old secret the moment the new one is issued.

The same registration can be scripted against Pulse's administration API; the request body and the response are in Pulse OAuth Clients.


Step 2: Request a token#

Ask Pulse for a token with the client credentials grant. The request is a form post, not JSON:

curl -s -X POST "https://<pulse-host>:4542/auth/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -d "grant_type=client_credentials&client_id=ErpImport&client_secret=<secret>"

Pulse answers with the token and its lifetime in seconds:

{
  "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Three things about the token are worth knowing before you build around it:

  • It lives for an hour unless the client's Token Expiry was changed at registration. Keep it and reuse it until shortly before expires_in runs out, then request another. Requesting a fresh token for every call works, but it is an extra round trip to Pulse each time.
  • It carries no role. A client credentials token identifies the application, never a person, so it is never an administrator. That is enough for the whole Atlas REST API, which asks only that the caller be authenticated. It is not enough for the write tools on Atlas's MCP server, which require a signed-in administrator; see Atlas MCP Server.
  • Clocks matter. Atlas allows no clock skew when it checks the token's lifetime. If the machine running your application and the Atlas host drift apart by more than a few seconds, calls start failing with 401 intermittently. Keep both on time synchronisation.

If you want to see what the token says, decode it with any JWT tool: sub is your client id, aud lists your client id followed by Atlas, and there is no role claim.


Step 3: Call Atlas#

Send the token as a bearer on every request. A first read that proves the whole chain:

curl -s --max-redirs 0 \
  -H "Authorization: Bearer <access_token>" \
  -H "Accept: application/json" \
  "https://<atlas-host>:4552/api/v1/domains"

A 200 with a JSON body means the client, the token and the audience are all right, and every other route under /api/v1 is open to the same token: domains, branches, libraries, ontology, instance maps, queries, compile. Writes work the same way, with one consequence to know about: Atlas records every change in Git, and the author of a commit made by your application is its client id. Give an import pipeline its own client and its changes are its own in the history.

Two habits save time later:

  • Do not follow redirects. On some deployments an unauthenticated API call answers with a redirect to the Pulse sign-in page rather than a 401. A client that follows it receives an HTML page where it expected JSON. Turning redirects off, as --max-redirs 0 does above, shows you the status instead.
  • Check the content type before parsing. text/html from an API route means a sign-in redirect or the interface shell, never an API response.

The same steps in Python#

The shape every language ends up with: one function that holds a token until it is about to expire, and one that sends it.

import time
import requests

PULSE = "https://<pulse-host>:4542"
ATLAS = "https://<atlas-host>:4552"
CLIENT_ID = "ErpImport"
CLIENT_SECRET = "<secret>"

_token = {"value": None, "expires": 0.0}

def token():
    if _token["value"] is None or time.time() > _token["expires"] - 60:
        r = requests.post(f"{PULSE}/auth/token", data={
            "grant_type": "client_credentials",
            "client_id": CLIENT_ID,
            "client_secret": CLIENT_SECRET,
        })
        r.raise_for_status()
        body = r.json()
        _token["value"] = body["access_token"]
        _token["expires"] = time.time() + body["expires_in"]
    return _token["value"]

def atlas(method, path, **kwargs):
    r = requests.request(method, f"{ATLAS}{path}", allow_redirects=False,
                         headers={"Authorization": f"Bearer {token()}",
                                  "Accept": "application/json"}, **kwargs)
    if "application/json" not in r.headers.get("Content-Type", ""):
        raise RuntimeError(f"{r.status_code} from {path}: not an API response")
    return r

print(atlas("GET", "/api/v1/domains").json())

What the token lets you do#

Surface With a client credentials token
/api/v1, every route Yes. Reads and writes, no role needed.
/mcp, read tools Yes, with the same token and the same Atlas audience.
/mcp, write tools No. These require a signed-in administrator.
Atlas's own interface No. The interface is a browser session, not a bearer.

Troubleshooting#

Symptom Likely cause What to do
Token request returns 400 with invalid_request The body is not form-encoded, the grant type is misspelt, or the id or secret is wrong Send Content-Type: application/x-www-form-urlencoded, spell client_credentials exactly, and compare the id and secret with the client's page in Pulse.
Token request succeeds but every Atlas call returns 401 Atlas is missing from the client's audiences Open the client in Pulse, add Atlas under Audiences, save, and request a new token. Tokens issued before the change do not gain the audience.
401 comes and goes Clock drift between your machine and Atlas Put both on time synchronisation. Atlas allows no skew.
401 on every call, audience is right The token has expired, or it was issued by a different Pulse than the one Atlas trusts Request a new token. If it still fails, confirm your Pulse address is the one in Atlas's Pulse settings.
The response is HTML Your client followed a redirect to the sign-in page Send a bearer on every call and turn off redirect-following.
Atlas's log shows a bearer rejection with a reason The token arrived and failed one check The reason names it: bad signature, wrong audience, wrong issuer, expired or malformed. Match it to the rows above.
Atlas's log says the Authorization header was absent Your client did not attach the token Check the header is set on the request that failed, not only on the first one.
The secret is lost It is shown only to administrators An administrator reveals it from the client's page in Pulse, or rotates it and gives you the new one.

esc
↑↓ navigate · ↵ open · ⌘K anywhere