Command line

Pre-release. Not published yet: use it from a checkout of the repository.

payrudder is a command line for the tenant API. It is the Go SDK behind a small set of commands, so it talks to the same /v1 routes your code does, with an API key. Use it to send a usage event by hand, watch a customer's usage move, receive webhooks on your own machine, set up endpoints, meters, plan versions and subscriptions, and look at customers and invoices without opening the console.

Install

It is not published yet (no release, no tag). Build it from a checkout of the repository; it needs Go 1.23 or newer and nothing else:

cd /path/to/payrudder/sdk/go
go install ./cmd/payrudder        # puts payrudder in $(go env GOBIN) or ~/go/bin
payrudder version

or run it without installing: go run ./cmd/payrudder <command>.

Sign in

payrudder login

asks for an API key (qk_test_… for the sandbox, qk_live_… for live), checks it with the API, and only then stores it. Nothing is stored when the API refuses the key. To keep the key out of the terminal, pipe it in:

printf '%s\n' "$PAYRUDDER_API_KEY" | payrudder login --api-key-stdin --base-url https://YOUR-API-ADDRESS

Every command below takes --base-url and prints what it does; the ones that list things take --json. payrudder <command> -h lists a command's flags.

Send a usage event

payrudder events send --customer cus_42 --meter api_calls --quantity 3 --property model=gpt-x
accepted   event 0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e

The event is sent as now, unless you pass --time (an RFC 3339 time, not older than 35 days). It carries an idempotency key, generated unless you give --idempotency-key, so repeating the command with the same key never counts twice (duplicate). A rejected event prints its error_code (see Errors) and the command exits 1.

Follow a customer's usage

payrudder events tail --customer cus_42
10:15:02  usage of cus_42 in 2026-11
10:15:02  api_calls                3 (3 events)
10:15:08  api_calls                5 (+2 events)

The API does not list events (they are counted, not kept for reading), so this asks for the customer's usage totals every two seconds (--interval) and prints a line when a meter moves. Stop it with Ctrl-C. --period 2026-10 follows another month.

Receive webhooks on your machine

payrudder webhooks listen --forward-to http://localhost:3000/webhooks/payrudder --secret whsec_…

starts a receiver on 127.0.0.1:4242. For every delivery it checks the signature with the endpoint's secret (--secret, or PAYRUDDER_WEBHOOK_SECRET), prints one line, and passes the delivery on to your app unchanged: the same body, the same Payrudder-Signature and event headers, so your own verification code runs for real. The answer your app gives is the answer Payrudder gets, so a handler that fails is retried by Payrudder as in production. A delivery whose signature does not verify is refused with 400 and never reaches your app.

Listening on http://127.0.0.1:4242 for Payrudder deliveries.
Verified deliveries are forwarded to http://localhost:3000/webhooks/payrudder.
10:21:40  invoice.paid   0192…  attempt 1  signature ok  -> HTTP 200 (4ms)

Payrudder pushes deliveries (the API does not hand out the delivered bodies, so there is nothing to fetch later), which means the address it posts to must reach your machine:

payrudder webhooks deliveries [--endpoint ID] [--state failed] shows how each delivery went (attempts, the answer of your endpoint); with --watch it keeps asking and prints each delivery that appears or changes. payrudder webhooks list lists the endpoints.

Send a test event

payrudder webhooks trigger ping

sends a signed ping to your endpoint (the only endpoint, or the one named with --endpoint) and waits for its answer: Delivered: your endpoint answered HTTP 200., or Not accepted: HTTP 500 …, which exits 1. ping is the only event the API can send on request; every other event happens when the thing it announces happens (an invoice is paid, a subscription is cancelled), and in the sandbox you can make it happen with Test mode.

Send a delivery again

payrudder webhooks redeliver 0192f0b4-6f6e-7c3a-9a3e-6a1f2b3c4d5e

takes the id of a delivery from payrudder webhooks deliveries and asks Payrudder to send it again from the start, whatever became of it (a delivery that failed for good, or one that was delivered and that your handler lost). The API queues it and answers at once; the command prints the id of the delivery and the endpoint, and payrudder webhooks deliveries --endpoint ID --watch shows how the endpoint answers. The event keeps its id, so a handler that ignores an event it has seen (see Idempotency) ignores this one too: redeliver to retry a failure, not to replay a success.

Customers and invoices

payrudder customers list --limit 10
payrudder invoices list --customer cus_42 --status open
payrudder invoices list --period 2026-10 --json
payrudder customers create --id cus_42 --name "Ada Lovelace" --email ada@example.com --country TR --locale tr --type individual
payrudder plans create --code pro --name Pro

customers create needs --id (your own id for the customer, the external_id), --name and --email; --country, --currency, --locale and --type are sent only when given. Creating a customer is not deduplicated by a key: the external id is the natural key, so running the command again answers 409 and exits 1 instead of making a second customer. plans create makes the plan itself, a code and a name; its prices and entitlements are a version, published with plans publish (next section). Both take --json.

Set up billing from the terminal

The commands that make the things a product is built from. All take --json and --base-url.

payrudder webhooks create --url https://example.com/webhooks/payrudder --events invoice.paid,invoice.voided
payrudder meters create --code api_calls --aggregation sum --value-property n --unit calls --filter model=gpt-x,gpt-y
payrudder plans publish --code pro -f pro-v1.json
payrudder subscriptions create --customer cus_42 --plan pro --interval year --trial-days 14

These calls have no idempotency key (see Idempotency), and for webhooks create, meters create and plans publish a repeat would make a second endpoint or another version. The command therefore does not retry them after a timeout or a 5xx: it prints the error and exits 1, and you look (payrudder webhooks list, the console) before running it again.

Tables show the first --limit rows (25 by default). Invoice totals are in the currency's minor units, as the API sends them.

Exit codes

Code Meaning
0 it worked (also Ctrl-C of a command that runs until stopped)
1 it failed: an API error (printed with its error key and request id, see Errors), a rejected event, an endpoint that did not accept the test event, a key the API refused
2 the command line could not be understood; nothing was sent

Not in it

Changing or deleting what you created (endpoints, meters, plans, subscriptions), coupons, listing meters and plans, the operator and console routes, and a trigger for events other than ping. Those are in the API, the SDKs and the console. This command line is pre-release like the SDKs.