Skip to content

Authentication

Four mechanisms are accepted, and they exist for different jobs. Pick by who is calling, not by which is easiest to paste.

Mechanism Use it for Not for
Personal access token your own scripts, CI, an MCP server on your machine anything you hand to a third party
OAuth2 a tool acting on somebody else's behalf server-to-server where you own both ends
JWT a first-party front end you are writing against your own account long-lived automation
Session the dashboard itself your code

API access is a paid feature

The API, the CLI and the MCP server require a plan that includes them (Pro and above). Requests from lower plans return 403. This is checked per request, so a downgrade takes effect immediately.

Personal access tokens

The straightforward option. Create one under Account → API Tokens (/accounts/tokens/), choose its scopes, and copy it — it is shown once.

curl https://withfeedback.com/api/v1/teams/ \
  -H "Authorization: Bearer spd_your_token_here"

Tokens are prefixed spd_. Give each token the smallest set of scopes that does its job: a token that can only read is a token that cannot be used to delete.

A token is a credential, not a config value

Treat it like a password. Keep it out of your repository, out of client-side JavaScript, and out of URLs — a token in a query string ends up in access logs and browser history. Put it in an environment variable or a secret manager, and send it in the Authorization header.

If a token leaks, delete it from the same page. Deletion takes effect at once; there is no cache to wait for.

OAuth2

Use this when a tool needs to act for a user who is not you — the user approves the scopes, and you never see their password or their token.

Two flows are supported:

For anything without a browser of its own: a CLI, an editor extension, a terminal on a server.

  1. POST /o/device-authorization/ — you get a verification URL and a short code.
  2. The user opens the URL, types the code, and approves the scopes.
  3. Poll POST /o/token/ until they approve.

The official public client id for the withfeedback CLI is 7yQMsnY2Is2f5tCuwwgItoQu3fRkEX2wnzIRj0Vh. It is public by design — device-flow clients hold no secret.

For web and mobile apps. PKCE is required; there is no implicit flow and no way to turn PKCE off.

Redirect URI schemes are restricted, and dynamic client registration is not open in production — a client is provisioned deliberately.

Access tokens last one hour. Refresh tokens last 30 days and rotate on every use: the response gives you a new refresh token, and the old one stops working. Store the new one, or your next refresh fails.

JWT

curl -X POST https://withfeedback.com/api/auth/token/ \
  -H "Content-Type: application/json" \
  -d '{"email":"you@example.com","password":"..."}'

Returns an access and a refresh token; refresh at /api/auth/token/refresh/, revoke at /api/auth/token/revoke/. Reasonable for a front end you own, unnecessary if a personal access token would do.

Two-factor accounts

If the account has MFA enabled, exchanging a password for a JWT is not the right path — use a personal access token or OAuth2 instead.

What a credential does not decide

Authentication says who you are. Two further checks still run on every request, and both can refuse you:

  1. Your scope — what this particular credential is allowed to touch. See Scopes.
  2. Your role in the team — owner, admin, member or viewer. A viewer's token with moderate:submissions still cannot approve anything, because the role does not permit it.

The narrower of the two wins, always. Widening a token's scopes never widens what its owner may do.