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:
PAPERMARK_TOKENenvironment variablePAPERMARK_CREDENTIALS_FILE— path to a JSON file- The config file (
~/.config/papermark/config.jsonon 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-MJHTThe 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:
| Flag | Default | Effect |
|---|---|---|
--scopes <list> | All read scopes + offline_access | Comma-separated scope list to request |
--no-browser | off | Don'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 --jsonThe 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 listThe 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 --stdinThe 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 --jsonInspecting credentials
$ papermark whoami
Token: pm_live_AbCdEfG…wXyZ
API URL: https://api.papermark.com
Source: PAPERMARK_TOKEN envThe token is partially masked. To get the full token (e.g. to copy to another machine), use:
papermark auth export --unmaskedOutput 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.jsonLogging out
papermark logoutClears 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 doctorRuns 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.