For years, the “Standard Operating Procedure” for Atlassian integrations was simple: create a fake user called jira_bot, buy it a license, and generate an API token that lasts indefinitely. It worked, it was convenient, and plenty of teams used it.
The shift from human “bot” accounts to purpose-built Service Accounts is not just a technical update; it is a move toward cleaner, more professional identity management. The catch is that it is often harder than it looks, because it forces you to rethink how your integrations are built and governed.
The old way of working often relied on “ghost identities”: human accounts used by machines or integrations. Today, this approach is generally considered outdated and no longer recommended, not only within Atlassian, but across many platforms and tools.
In practice, three problems kept coming back:
If a colleague asked me about the migration, this is what I would bring up first. Moving to Service Accounts changes how your integrations communicate, and unlike human API tokens, service account credentials follow a stricter routing and security model.
Many development teams spend days debugging 404 Not Found errors because they try to use their old site URLs. Service Accounts cannot talk to <your-site>.atlassian.net. You must use the Global API Gateway: api.atlassian.com.
Legacy Bot User
Base URL: <your-site>.atlassian.net/rest/api/3/...
Routing: Direct to site
Service Account
Base URL: api.atlassian.com/ex/jira/{cloudId}/rest/api/3/...
Routing: Requires a Cloud ID lookup
Cloud ID = a unique GUID for your site.
Found by calling: GET https://<your-site>.atlassian.net/_edge/tenant_info
Immutable scopes
With legacy tokens, “admin” was “admin.” With Service Accounts, you must select specific API Scopes (like read:jira-work) during creation.
Scopes are immutable. Once a credential is created, its scopes cannot be modified. If an integration evolves and requires additional access, the existing credential must be revoked, a new one created, and the integration updated accordingly.
There are two common approaches to handle this:
Revoke the existing credential and issue a new one with the required scopes.
Maintain multiple credentials, each with a clearly defined and limited scope, aligned to specific use cases.
The dual-gate authorization model
A request must pass two independent gates:
The Scope Gate (The Token): The created token itself has permission to perform the action (like read:jira-work)
Atlassian offers two ways to authenticate your Service Account.
Option A: API Tokens (Bearer Auth)
Static, scoped credentials linked to a service account, used for simple and direct API access.
Best suited for legacy tools or low-complexity integrations. Tokens expire between 1 and 365 days.
# Bearer Auth using the generated API Token
## URLs for API tokens with scopes for Jira
curl -v \
https://api.atlassian.com/ex/jira/{cloudId}/rest/api/3/project \
-H "Authorization: Bearer my-api-token" \
-H "Accept: application/json"
## URLs for API tokens with scopes for Confluence
curl -v \
https://api.atlassian.com/ex/confluence/{cloudId}/wiki/rest/api/space \
-H "Authorization: Bearer my-api-token" \
-H "Accept: application/json"
Option B: OAuth 2.0 Credentials (Bearer Auth)
Dynamic, short-lived access tokens with enforced scopes, designed for secure and governed integrations.
This is the recommended standard for secure machine-to-machine communication.
# Step 1: Exchange credentials for a 60-minute token
curl --request POST \
--url "https://api.atlassian.com/oauth/token" \
--header "Content-Type: application/json" \
--data '{
"grant_type": "client_credentials",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}'
# Step 2: Use the Access Token in the header
curl --request GET \
--url "https://api.atlassian.com/ex/jira/YOUR_CLOUD_ID/rest/api/3/issue/PROJ-123" \
--header "Authorization: Bearer YOUR_ACCESS_TOKEN"
Refactoring your scripts requires managing new operational realities:
read:issue:jira, write:comment:jira) before creation, because scopes lock once saved.The transition to Service Accounts moves from implicit trust to explicit control. While it requires more discipline upfront (mapping scopes, discovering Cloud IDs, and refactoring base URLs), it eliminates "zombie" licenses and strengthens your security posture. Treat your integrations as first-class services, and the security debt of the "bot user" era will become history.