Skip to main content
Meridian fetches your open Jira tickets, stores them locally as pm_tasks, and uses them during session classification. Every time you close a coding or research session, Meridian’s classifier checks which ticket that work most likely belongs to and writes a ticket_links row to its database — no timesheets, no manual updates required. Meridian supports two ways to authenticate to Jira Cloud:
  • Browser OAuth (recommended) — one command, no API token, your site is discovered automatically, and access tokens auto-refresh.
  • Static API token (fallback) — three environment variables. Use this when your Atlassian org blocks third-party OAuth apps or you need a fully scripted, headless setup.
If both are configured, OAuth wins.

Prerequisites

  • A Jira Cloud account (Jira Data Center is not supported)
  • Meridian installed and the daemon running (meridian status to confirm)
  • For OAuth: a desktop browser on the machine running Meridian
The installer offers OAuth inline during meridian setup / ./install.sh. You can also run the login at any time after install:
meridian oauth-login jira
The command:
  1. Generates a PKCE verifier and opens your default browser to Atlassian’s consent screen.
  2. Waits for you to click Accept. Atlassian redirects back to a short-lived loopback server at http://127.0.0.1:9123/callback.
  3. Exchanges the authorization code for an access token and a refresh token.
  4. Writes them to ~/.meridian/oauth/jira.json (mode 0600, owner-only readable).
After the browser shows “Login successful, you can close this tab”, restart the daemon so it picks up the new credentials:
meridian restart
That’s it — your site is discovered automatically from the account you sign in with. There is nothing to add to ~/.meridian/.env.

Scopes requested

The Atlassian consent screen lists the four scopes Meridian asks for:
ScopeWhy Meridian needs it
read:jira-workRead open issues you’re assigned to.
write:jira-workPost worklogs to issues you classify against (only after you approve a draft in the dashboard).
read:jira-userUsed by meridian doctor’s /myself health probe to confirm the token works.
offline_accessIssues a refresh token so the daemon can mint new access tokens in the background — without this, you’d be re-prompted every hour.
Meridian never creates, modifies, closes, or deletes tickets on its own. The only write call is a draft worklog you have explicitly approved in the dashboard.

The token store

Tokens live at ~/.meridian/oauth/jira.json:
~/.meridian/oauth/jira.json   # mode 0600, owner read/write only
The file is created with restrictive permissions up front. The daemon refreshes the access token before each request when it’s near expiry and persists the rotated refresh token back to the same file — Atlassian rotates refresh tokens on every refresh, so the store must be writable by the daemon. To revoke access, either delete the token store and run meridian restart, or revoke the grant from your Atlassian account’s connected apps page. The next daemon start will fall back to static API-token auth if it is configured, otherwise it will report jira: not configured.

Override the OAuth client ID

Meridian ships with a public Atlassian client ID baked in — PKCE has no client secret, so there is nothing to protect. If your organisation runs its own Atlassian app (for example, to scope OAuth to an internal site allowlist), override the baked-in value:
# ~/.meridian/.env
JIRA_OAUTH_CLIENT_ID=your-atlassian-app-client-id
Then re-run meridian oauth-login jira.

Connect with a static API token (fallback)

Use this path if meridian oauth-login jira fails with an org-approval error (“site admin must authorize this app” or user app installs disabled), or when running Meridian in a headless environment where you cannot open a browser.
1

Create a Jira API token

Navigate to id.atlassian.com/manage-profile/security/api-tokens, click Create API token, give it a descriptive label such as meridian-local, and copy the value. Atlassian only shows the token once.
2

Add the credentials to ~/.meridian/.env

Run meridian config edit and add the Jira block:
# ~/.meridian/.env

JIRA_BASE_URL=https://your-org.atlassian.net
JIRA_EMAIL=you@your-org.com
JIRA_API_TOKEN=your-api-token-here

# Optional: restrict sync to specific project keys (comma-separated)
# JIRA_PROJECT_KEYS=KAN,ENG
3

Restart the daemon

meridian restart
All three variables must be present for the basic-auth path to activate. Static-token requests go directly to your JIRA_BASE_URL; OAuth requests instead go through https://api.atlassian.com/ex/jira/{cloudId} with a Bearer token. Both paths share the same request layer, so the rest of Meridian — classification, doctor, worklog drafts — works identically.

How Meridian picks an auth path

On every Jira request, the daemon resolves credentials in this order:
  1. If ~/.meridian/oauth/jira.json exists, use OAuth. Refresh the access token first if it’s near expiry.
  2. Otherwise, if JIRA_BASE_URL, JIRA_EMAIL, and JIRA_API_TOKEN are all set, use basic auth.
  3. Otherwise, report jira: not configured and skip Jira sync.
This means an OAuth-connected installation can also keep static creds in .env as a documented fallback — they will simply be ignored as long as the token store exists.

Verify the connection

1

Run a health check

meridian doctor
Look for jira: connected in the output. The check calls Jira’s /myself endpoint, which works under both OAuth (via the read:jira-user scope) and basic auth.
2

Inspect the task cache (optional)

sqlite3 ~/.meridian/meridian.db \
  "SELECT task_key, title, status FROM pm_tasks LIMIT 10;"
You should see rows for your open issues.

Limit which projects are synced

By default Meridian fetches every open ticket assigned to you. To restrict it to one or more projects, set JIRA_PROJECT_KEYS to a comma-separated list of project keys:
JIRA_PROJECT_KEYS=KAN,ENG
JIRA_PROJECT_KEYS applies to both auth paths.

Force-refresh the Jira task cache

The daemon refreshes pm_tasks automatically at startup and on its background poll cycle. To pull the latest data on demand without restarting, run:
meridian tasks-sync
This bypasses the 5-minute staleness gate and re-syncs every configured tracker (Jira, GitHub, Linear) in one shot.

Troubleshooting

Some Atlassian organisations require a site admin to authorize third-party OAuth apps before any user can grant consent. If you see “site admin must authorize this app” or “user app installs are disabled”, switch to the static API token path — basic auth works without org-level approval.
Atlassian only issues a refresh token when the offline_access scope is granted. If the consent screen omits it, the daemon refuses the response so you don’t end up with a token that silently expires after an hour. Re-run meridian oauth-login jira and make sure all four scopes are listed on the consent screen.
Double-check that:
  • JIRA_EMAIL matches the email address on your Atlassian account exactly.
  • JIRA_API_TOKEN is the token value itself, not a label or partial string.
  • JIRA_BASE_URL ends with .atlassian.net and has no trailing slash.
Regenerate the API token in Atlassian account settings if you’re unsure.
An empty pm_tasks table means the JQL query returned no issues. Check that:
  • Your account has at least one open issue assigned to it in the targeted projects.
  • If you set JIRA_PROJECT_KEYS, confirm the keys are correct (they are case-sensitive, e.g. KAN not kan).
Run meridian tasks-sync to force a re-sync after fixing the configuration.
Jira is considered configured when either:
  • The OAuth token store exists at ~/.meridian/oauth/jira.json, OR
  • All three of JIRA_BASE_URL, JIRA_EMAIL, and JIRA_API_TOKEN are set in ~/.meridian/.env.
Run ls -l ~/.meridian/oauth/jira.json to check for the token store, or meridian config edit to confirm the static creds.