Skip to content

CLI

pip install withfeedback
# or, without installing:
uvx withfeedback --help

The command is withfeedback.

Signing in

Create one under Account → API Tokens, then either:

export WITHFEEDBACK_TOKEN=spd_...

or store it in the OS keychain:

withfeedback login --token spd_...
withfeedback login

Prints a URL and a short code, waits while you approve in the browser, and stores the tokens in your OS keychain — macOS Keychain, Windows Credential Locker, Secret Service or KWallet on Linux. Never a plaintext file. Access tokens refresh transparently.

withfeedback logout   # remove stored tokens

Resolution order for every command: --token flag, then WITHFEEDBACK_TOKEN, then the keychain.

Commands

withfeedback testimonials list    --team <ID> --project <ID> [--status pending|approved|rejected|spam] [--rating N] [--q text] [--json]
withfeedback testimonials approve <SUBMISSION_ID> --team <ID>
withfeedback testimonials reject  <SUBMISSION_ID> --team <ID>
withfeedback testimonials create  --team <ID> --project <ID> --text "..." [--rating N] [--name ..] [--email ..] [--consent]

withfeedback import csv FILE --team <ID> --project <ID> --text-col COL [--rating-col COL] [--name-col COL] [--email-col COL] [--consent]

withfeedback surveys list --team <ID> --project <ID>
withfeedback nps summary  --team <ID> --project <ID>
withfeedback widgets list --team <ID> --project <ID>
withfeedback usage        --team <ID>

Human-readable tables by default. --json gives you the raw API response, with errors still on stderr — so | jq works and a failure still fails.

Importing a CSV

withfeedback import csv reviews.csv \
  --team <TEAM_ID> --project <PROJECT_ID> \
  --text-col "Testimonial" --rating-col "Stars" \
  --name-col "Author" --email-col "Email" --consent

The file is validated locally first — headers and row count — before anything is uploaded, so a wrong --text-col fails in a second instead of after the upload. Rows are processed asynchronously and land as pending; the command prints the batch id and a URL to poll.

--consent is a factual claim

Pass it only when display consent was genuinely collected for those rows. It is the flag that decides whether these testimonials may appear on your site, and nobody downstream can check it for you.

In CI

- name: Import weekly reviews
  env:
    WITHFEEDBACK_TOKEN: ${{ secrets.WITHFEEDBACK_TOKEN }}
  run: |
    uvx withfeedback import csv reviews.csv \
      --team $TEAM --project $PROJECT --text-col Review --json

Give the CI token only write:feedback plus the job scopes. A token that cannot approve is a token that cannot publish something nobody read.