PapermarkDocs

Authentication

CLI login flows, `PAPERMARK_TOKEN` env var, and credential files.

The CLI looks for a token in three places, in this order. The first one found wins:

  1. PAPERMARK_TOKEN environment variable
  2. PAPERMARK_CREDENTIALS_FILE — path to a JSON file
  3. The config file (~/.config/papermark/config.json on macOS/Linux, roaming AppData on Windows)

papermark whoami shows you which source the active token is coming from, plus the API URL.

Per-machine: device flow

$ papermark login
Open https://app.papermark.com/oauth/device in your browser.
Enter code: WDJB-MJHT

The CLI calls the OAuth 2.1 device authorization endpoint, prints the code, polls for approval, then writes the access token (and refresh token, if offline_access was granted) to the config file at 0600.

Flags:

FlagDefaultEffect
--scopes <list>All read scopes + offline_accessComma-separated scope list to request
--no-browseroffDon't auto-open the browser; just print the URL

Per-machine: paste a token

If you already have a pm_live_… token from the dashboard:

papermark login --token pm_live_…

Validates the token with one API call, then stores it. Same end state as the device flow, no browser involved. Good for CI runners that already have the secret in their environment.

CI patterns

export PAPERMARK_TOKEN=pm_live_…
papermark documents list --json

The CLI never writes the env-var token to disk. It lives in the process environment for the duration of the call. Easiest pattern; use it unless you have a reason not to.

- name: List documents
  run: papermark documents list --json
  env:
    PAPERMARK_TOKEN: ${{ secrets.PAPERMARK_TOKEN }}

Mint a pm_live_… token in the dashboard, store it in Settings → Secrets and variables → Actions → New repository secret as PAPERMARK_TOKEN. Done.

If your CI provider mounts secrets as files (Vault, some Kubernetes setups), point at the file:

export PAPERMARK_CREDENTIALS_FILE=/run/secrets/papermark.json
papermark documents list

The JSON shape:

{
  "token": "pm_live_…",
  "apiUrl": "https://api.papermark.com"
}

Both fields are read once per process. apiUrl is optional; defaults to production.

For setups where you'd rather not have the token sit in an env var or file at all (short-lived workers, secret-rotation scripts), pipe it in:

echo "pm_live_…" | papermark auth set --stdin

The token is validated and persisted to the config file in one step, without a tempfile. Pass JSON instead of a bare token if you also want to set apiUrl:

echo '{"token":"pm_live_…","apiUrl":"https://api.papermark.com"}' \
  | papermark auth set --stdin --json

Inspecting credentials

$ papermark whoami
Token:    pm_live_AbCdEfG…wXyZ
API URL:  https://api.papermark.com
Source:   PAPERMARK_TOKEN env

The token is partially masked. To get the full token (e.g. to copy to another machine), use:

papermark auth export --unmasked

Output is JSON; redirect to a file and copy the file securely. Don't paste the unmasked output into chat.

Moving credentials between machines

# On source machine
papermark auth export --unmasked > /tmp/pm.json

# Copy /tmp/pm.json over scp/airdrop, then on target:
papermark auth set --stdin --json < /tmp/pm.json

Logging out

papermark logout

Clears the token (and refresh token) from the config file. To revoke the token server-side too, do it from the dashboard's Settings → API Tokens. papermark logout only forgets the local copy.

Troubleshooting

papermark doctor

Runs a sequence of checks: config file readable, token present, API reachable, token still valid. Returns a JSON array of pass/fail entries when piped or --json'd. Use it as the first step when something stops working.

On this page