Commands
Every operation the management API exposes is reachable from a command, and a test in the CLI’s repository fails the build if a new endpoint ever ships without one.
Run keylight <group> --help for the flags on any group, and keylight <group> <command> --help
for a single command. Nothing here prompts: every input is a flag.
Apps and key types
Section titled “Apps and key types”keylight products listkeylight products get my-appkeylight products create --display-name "My App" --key-prefix MYAP --support-email help@myapp.comkeylight products update my-app --purchase-url https://myapp.com/buykeylight products free-tier set my-app --enabledkeylight products test-mode disable my-appupdate accepts the support email and the purchase URL. Those are the only two fields the API
changes on an existing app.
Key types live under the app that owns them:
keylight products key-types list --product my-appkeylight products key-types create --product my-app --key-type-id pro --display-name "Pro"keylight products key-types update --product my-app --key-type-id pro --activation-limit 5keylight products key-types verify-prices --product my-appkeylight products key-types delete --product my-app --key-type-id proverify-prices checks that every mapped payment-provider price still resolves — worth running
after you edit prices at the provider, since a stale mapping only shows up at checkout.
Enabling Stripe test mode needs two secrets, read from environment variables you name rather than passed as arguments:
export KEYLIGHT_STRIPE_TEST_SECRET_KEY=sk_test_...export KEYLIGHT_STRIPE_TEST_WEBHOOK_SECRET=whsec_...keylight products test-mode enable my-appLicenses
Section titled “Licenses”keylight licenses list --limit 10keylight licenses get <license-id>keylight licenses create --product my-app --key-type pro --customer-email buyer@example.comkeylight licenses remint <license-id>keylight licenses deactivate-device <license-id> --instance-id <instance-id>keylight licenses import --product my-app --file ./rows.jsonkeylight licenses revoke <license-id>keylight licenses exportKeys are masked in list output, so a screenshot or a shared terminal does not leak one. The raw key is returned once, when it is minted or reminted.
create accepts an --idempotency-key. Reuse it across retries and a repeated call cannot issue
a second license; omit it and one is generated per invocation.
Use --send-email on create if you want Keylight to email the key to the customer. Without it,
nothing is sent.
The import file
Section titled “The import file”import is the one command whose input you have to author, so it is the one place a wrong guess
costs you a round trip. The file is a JSON array of rows. email and key_type are required;
license_key, name and expires_at are optional. The field names are snake_case here, unlike
every other payload:
[ { "email": "buyer@example.com", "key_type": "pro", "license_key": "OLD-KEY-0001", "name": "Ada" }, { "email": "second@example.com", "key_type": "pro" }]Give license_key to carry an existing key across from a previous vendor; omit it and Keylight
mints a fresh one. --migration-source tags the whole batch with where the keys came from, which
is what later tells an imported license apart from one you issued.
Customers
Section titled “Customers”keylight customers list --limit 10keylight customers get <customer-id>keylight customers create --email buyer@example.com --name "Buyer"keylight customers link-license <customer-id> --license <license-id>keylight customers entitlements set <customer-id> --license <license-id> --entitlements '["sync","export"]'entitlements set replaces the entitlements on that license rather than adding to them, so pass
every one the customer should end up holding.
Payment integrations
Section titled “Payment integrations”keylight integrations listkeylight integrations statuskeylight integrations connect <provider>keylight integrations disconnect <provider>keylight integrations rotate-secret <provider>list shows every provider and whether it is connected; status shows the Stripe connection in
detail, including which account and whether it is live or sandbox.
keylight usagekeylight usage --days 30Activations, validations, active devices, and monthly actives — the same figures the dashboard reports.
SDK key and webhook
Section titled “SDK key and webhook”keylight sdk-key getkeylight sdk-key rotatekeylight webhook getkeylight webhook set --url https://example.com/keylightTest purchases
Section titled “Test purchases”keylight test-purchase create --product my-app --key-type prokeylight test-purchase get <run-id>Runs a purchase end to end and lets you inspect what it issued, without a real card.
Operations that need a human
Section titled “Operations that need a human”Seven operations are dangerous enough that a token cannot run them on its own, however broad its scopes. A human approves the specific call in a browser first — the same shape as npm’s web-based two-factor login.
| Command | Why it is gated |
|---|---|
licenses revoke | A customer loses access, and it cannot be undone |
licenses export | Bulk customer data leaves the account |
products key-types delete | Cannot be undone |
integrations rotate-secret | The current ping URL stops working immediately |
sdk-key get | A live secret leaves the dashboard |
sdk-key rotate | Every shipped build embedding the old key fails until you release an update |
webhook set | Redirects where your events are delivered |
When a command hits one, the CLI prints an approval URL (and opens it, unless --no-browser is
set or stdout is not a terminal), polls until you approve or deny, and then retries the original
call automatically. You never have to know which commands are gated — it walks the flow whenever
the server asks for it.
These block and wait rather than failing fast. They are human-gated by design, and an unattended caller failing instantly would misrepresent what they are; an agent surfaces the URL to its user and waits. Approving grants that one request, not standing permission — the confirmation is consumed on use, and the server checks that the retried request matches the one you approved.
Scripting
Section titled “Scripting”Every command takes --json:
keylight licenses list --json | jq -r '.items[] | select(.status == "active") | .displayKey'List commands are cursor-paginated. Human output tells you when there is more and prints the
command to continue; --json responses carry nextCursor, which is null on the last page.
keylight licenses list --limit 100 --cursor <cursor>