CLI
The Flint CLI (flint) is a terminal client for the public API. Each API command maps to a documented /v1 route, and flint api calls any documented route directly, so anything you can do over HTTP you can do from a shell, a script, or a CI job.
It is most useful for three things:
- Trying a payment flow without writing an integration first.
- Forwarding live sandbox webhook events to a local server with
flint listen. - Scripting the API in CI, where JSON output and stable exit codes matter more than pretty printing.
Install#
npm install -g @flintpay/cli
The npm package resolves a prebuilt binary for your platform. On macOS and Linux you can install it with Homebrew instead:
brew install flint-pay/tap/flint
Prebuilt archives for macOS, Linux, and Windows are also attached to each release, with a checksums.txt to verify against. Each release also publishes a container image to ghcr.io/flint-pay/flint-cli.
To install without npm or Homebrew, download and verify the archive with the install script:
curl -fsSL https://raw.githubusercontent.com/flint-pay/flint-cli/main/scripts/install.sh -o /tmp/flint-install.sh
FLINT_INSTALL_DIR="$HOME/.local/bin" sh /tmp/flint-install.sh
Set FLINT_CLI_VERSION=X.Y.Z to pin a version, and add the install directory to your PATH. On Windows, download the ZIP and checksums.txt from the release, check the ZIP with Get-FileHash -Algorithm SHA256, and extract flint.exe into a directory on PATH.
Verify the install:
flint version
Authenticate#
Sign in through the browser. One login can authorize several contexts, where a context is one merchant and environment, plus a sandbox for test access.
flint login
flint context list
flint context switch
flint login is short for flint auth login. The browser page shows the same code as your terminal and preselects your default sandbox. Add merchants, sandboxes, or live access before approving; live access is labeled and needs an explicit approval. Initial selection defaults to sandbox. Use flint login --live to request an initial live context.
flint login reuses an authenticated session. Use --new-session to replace it after successful browser approval, --scope to request a permission beyond the default set, --no-open to open the link yourself, and --profile acme for a separate profile.
Select a context#
flint context list shows currently authorized contexts. Switch the profile's default interactively or by ID:
flint context switch ctx_123
flint context switch ctx_live123 --live
The interactive list labels LIVE access. Selecting a live default by ID requires --live. Commands in a live context you approved at login do not repeat --live; sensitive and destructive confirmations still apply. A live API key, imported or set in FLINT_API_KEY, needs --live on every command (see Sandbox and live safety).
Use a context for one command without changing the default:
flint payment-intents list --context ctx_123
Projects can pin a context in .flint/config.json:
{"context": "ctx_123"}
Selection uses the explicit --context flag first, then the project's context, then the profile default. Switching the default does not redirect an already-running command, pagination, or webhook listener. Listing or selecting a context does not grant permission.
Review or end access#
flint reauth
flint logout --confirm
Reauthorization reviews consent for the same session and can add or remove contexts and reduce permissions. To add one permission to the active context without reviewing the rest, run flint reauth --scope commerce.subscriptions.read. Browser denial leaves the existing session unchanged.
The CLI stores credentials in your OS keychain and refreshes automatically. Credentials never go in config files. Access tokens last 15 minutes. Sessions expire after 30 days without refresh or 90 days after login. A lost refresh response can require a new login; reusing an old refresh token revokes the session.
Logout revokes access across every context before deleting local credentials. If revocation fails, credentials remain available for retry. You can also review and revoke CLI sessions in the dashboard. See CLI OAuth for the HTTP contract.
Manual keys and CI#
To use an existing API key, run flint auth import and paste the key at the hidden prompt. It validates the key and stores it in your OS keychain. API-key logout only removes the local credential. For a script that imports a key:
flint auth import --stdin < key.txt
Use FLINT_API_KEY for CI credentials. Keep keys in your CI secret store.
If you do not have an account yet, flint signup runs the same API-first onboarding flow described in API & agent onboarding: it collects your email, sends a verification code, completes machine-actionable onboarding steps, and stores the initial sandbox key in your OS keychain as soon as the API makes it available. The key is never printed, so keep using flint commands with it; to call the API with curl or an SDK, create a key on the dashboard's API keys page. If a requested capability requires an embedded human verification step first, the command returns that onboarding state as an actionable error. If the email already has a Flint account, or the account already issued its first key, flint signup stops and tells you to run flint login instead.
Then confirm everything is wired up:
flint doctor
doctor checks your config, credential, connectivity, environment, merchant, scopes, and the server’s current API version. A different API version is informational: the output names the server version, the version your CLI requests, and the changelog URL. The CLI keeps requesting its bundled version. Failed checks include a fix. flint doctor --fix applies only the mechanical config repairs it proposed; it never touches credentials or server state.
flint init is the guided entry point: it runs the doctor checks if you are authenticated and prints the next steps below, or points you at auth login and signup if you are not.
Your first payment#
Create a payment link and print its URL:
flint payment-links create \
--name "Design consultation" \
--item-name "Design consultation" \
--amount 2500 \
--currency USD \
--field data.url
Open the URL in a browser and pay with test card 4242 4242 4242 4242, any future expiry date, and any CVC. The payment then appears under Payments in the dashboard for your sandbox. Accept your first payment walks through the same payment with curl.
Amounts are integers in the currency's smallest unit, so 2500 is $25.00. See Money & currency.
A payment link is reusable. For a one-time checkout page that opens in your browser, create a checkout session instead:
flint checkout create \
--quick-pay-name T-shirt \
--amount 2500 \
--currency USD \
--open
To drive a payment entirely from the terminal, create a payment intent and confirm it with a sandbox payment source token:
flint payment-intents create --amount 2500 --currency USD --payment-option card
flint payment-intents confirm @last.pi --payment-source-token pm_card_visa
flint help test-cards prints the sandbox tokens and their matching test card numbers offline. More scenarios, including declines and 3D Secure, are in Testing.
For the full commerce flow, create an order, then pay its balance with a sandbox token. order.json holds the order create body:
{
"line_items": [{
"name": "Design consultation",
"quantity": 1,
"unit_price_money": {"amount": 2500, "currency": "USD"}
}],
"tax": {"enabled": false}
}
flint orders create --input order.json
echo '{"action": "pay", "payment_source": {"token": "pm_card_visa"}}' | flint orders pay @last.ord --input -
--payment-source-token on flint orders pay is a shortcut for a different action: it confirms a payment intent that already exists on the order, written as pi_123=pm_card_visa, or as pm_card_visa alone when the order has exactly one payment intent waiting for confirmation.
Any command that takes a request body accepts --input file.json, or --input - to read from stdin.
Referring to the last resource#
The CLI records the IDs it creates and reads, so you can chain commands without copying IDs:
flint payment-intents create --amount 2500 --currency USD --payment-option card
flint payment-intents get @last.pi
@last.pi resolves to the most recent payment intent for the current profile and environment. The qualifier is the resource's ID prefix, so @last.ord is the last order and @last.cs the last checkout session. flint history lists what is stored; flint history --clear --confirm empties it.
Forward webhooks to localhost#
flint listen opens a stream of your sandbox's webhook events and forwards each one to a local URL, so you can develop against real events without a public tunnel:
flint listen --forward-to http://localhost:8080/webhooks/flint
It prints a signing secret on startup:
Webhook signing secret: whsec_...
Forwarding to http://localhost:8080/webhooks/flint
Forwarded requests are signed with that secret, so your local handler can run the same signature verification it runs in production. The secret is generated per session, so set it in your local environment each time you start listening. See Webhooks for the verification details.
Narrow the stream to the events you care about, and resume where you left off after a restart:
flint listen \
--forward-to http://localhost:8080/webhooks/flint \
--event-type payment_intent.succeeded \
--cursor whev_123
The forward target must be a local address. flint listen is a development tool, not a delivery mechanism: to deliver events to a real service, register a webhook endpoint.
Waiting for async results#
Payments, refunds, and payouts settle asynchronously. Rather than polling by hand, block until a resource reaches the state you expect:
flint payment-intents get pi_123 --wait-for status=succeeded --for 60s
The command polls with backoff and exits 0 once the field matches. If the deadline passes first it exits 6 and still prints the last state it saw, so a CI job can tell "not yet" apart from "failed".
Scripting and CI#
For bounded commands, --output json prints one JSON envelope on stdout and switches off every prompt, so commands never block waiting for a TTY:
flint payment-intents list --status succeeded --output json
Streaming commands emit one listener, webhook event, delivery result, or checkpoint object per line. flint listen --output json requires --max-events or --for, which prevents automation from waiting forever. Use --output ndjson to opt into an intentionally unbounded stream; it still accepts either bound when you want one.
Diagnostics, progress, and prompts always go to stderr, so stdout stays a clean data stream. Useful flags when piping:
--field PATHprints one value, unquoted, for direct capture into a shell variable.--select FIELDSnarrows the output to specific fields.--jq EXPRapplies a jq expression to the result.--allfollows pagination and returns every page (see Pagination).--idempotency-key KEYsets the idempotency key on a write.--dry-run=clientvalidates and prints the request the CLI would send, without calling the API.
Exit codes are stable, so scripts can branch on the failure class instead of parsing stderr:
| Code | Meaning |
|---|---|
0 | Success |
1 | The API returned an error |
2 | Usage error (bad flag, missing argument) |
3 | Authentication or configuration problem |
4 | Confirmation required and not given |
5 | Network or connectivity failure |
6 | --wait-for timed out before the condition was met |
70 | Internal CLI error |
flint help exit-codes prints the same catalog offline.
In CI, supply the key through the FLINT_API_KEY environment variable instead of the keychain. Set FLINT_OUTPUT=json and FLINT_NO_INPUT=1 to make every command non-interactive by default. FLINT_PROFILE selects a profile, FLINT_MERCHANT sets the merchant guard, and FLINT_BASE_URL points the CLI at a different server; test and live requests otherwise both go to https://api.withflintpay.com, and the authenticated credential decides the environment. OAuth credentials are bound to their issuing API URL; a conflicting FLINT_BASE_URL is rejected before a token is sent.
Sandbox and live safety#
The CLI derives the environment from the key itself, so there is no separate mode flag to get out of sync.
Live credentials require you to acknowledge production explicitly. Any command run with a flint_live_... key fails with LIVE_ACKNOWLEDGEMENT_REQUIRED until you pass --live. A live context from flint login was approved in the browser, so its commands run without --live. On top of that, destructive commands and sensitive writes in live mode prompt for confirmation; pass --confirm to acknowledge in advance, or --preview to see what would be affected without doing it.
mode=live on /v1/oauth/authorize counts as a sensitive write even though the route is a GET, because it creates a real partner grant. It needs --live and a confirmation, including through flint api:
flint api get "/v1/oauth/authorize?mode=live&client_id=FPC_CLIENT_ID&..." --live --confirm
For shared scripts, pin a profile to a specific merchant so a misconfigured key cannot act on the wrong account:
flint config set merchant mer_123
If the credential's merchant does not match the guard, the command fails before sending a request.
To check the guard into a repository instead of each developer's machine, commit .flint/config.json at the project root. The CLI walks up from the working directory to find it:
{ "profile": "acme", "merchant": "mer_123", "sandbox": "test_01JQEXAMPLE..." }
Later sources win: the global config first, then the project file, then FLINT_PROFILE and FLINT_MERCHANT, then --profile and --merchant. sandbox is project-only and takes a test_ ID. flint config get prints the resolved values and, in sources, where each one came from. The file holds no secrets; credentials stay in the keychain or FLINT_API_KEY.
Raw API access#
When a route has no dedicated command, call it directly:
flint api get /v1/payment-intents/pi_123
flint api post /v1/payment-intents --input payment-intent.json
flint api get /v1/orders --paginate
flint api is an escape hatch over the documented public API, not an internal transport. It enforces the same auth, live-mode acknowledgement, confirmation, and error handling as first-class commands. --paginate follows next_page_token and emits one page envelope per line as NDJSON. Both --paginate and --all require a read-only flint api get. Anything that writes, including a live OAuth authorization, fails with PAGINATION_REQUIRES_GET before a request goes out.
Debugging#
--debug prints the API version and trace ID for a request. When Flint returns an API error, the CLI prints its request_id. Find the request's log by that ID, then open the log by its api_request_log_id, which starts with rlog_:
flint request-logs list --request-id bce56cba-0827-44aa-bb56-4f200ba15ee6
flint request-logs get rlog_123
flint request-logs list --status-bucket server_error lists recent failures instead.
flint timeline shows the full lifecycle of any supported resource, which is usually the fastest way to understand why something is in an unexpected state:
flint timeline pi_123
See Debugging for the wider request-tracing story.
Getting help#
flint help search searches Flint Help, Flint's community and support site, for answered questions and existing threads. It sends no credential, so it works before you authenticate:
flint help search webhook signature verification
Each result carries the thread URL, product area, reply count, and whether it is answered. Add --output json to read them in a script.
When no thread covers it, flint support open builds a composer link with your terminal context filled in and opens it in your browser. You post from there:
flint support open --request-id req_123 --area webhooks
The link carries the request ID, the resource ID, the product area, and the environment your credential belongs to. --title and --body prefill the thread, --private asks for a private thread instead of a public one, --ai-agent marks the thread as opened by an agent rather than a person, and --no-open prints the link without opening a browser. --area accepts checkout, payments, payouts, invoices, subscriptions, payment_links, api, webhooks, dashboard, documentation, flint_billing, account, or sales.
Agents and MCP#
flint mcp serve runs a local MCP server over stdio that exposes commands safe for structured, non-interactive use as MCP tools, so an AI agent can act on your Flint account with your credential. Tool names are canonical command names such as payment-intents.create, and tool inputs are generated from the same command schemas. Import the credential before starting the server or provide it through FLINT_API_KEY; auth.import is intentionally not an MCP tool because secret keys must not pass through agent arguments.
flint mcp serve
This is different from the hosted MCP server, which is read-only and covers documentation. Use the hosted server so an agent can read docs and schemas; use flint mcp serve so an agent can create and inspect real resources in your sandbox.
For agents that call the CLI directly rather than over MCP, the schema commands make the surface machine-readable without scraping help text:
flint schema commands --output json
flint schema input payment-intents.create --output json
flint schema output payment-intents.create --output json
flint schema errors --output json
flint schema events --output json
An agent can list commands, fetch the input schema for the one it wants, build a request body mechanically, and validate it before spending a call. See AI agents.
Command reference#
CLI commands lists commands with their arguments, flags, examples, and API routes. Run flint schema commands --output json to list the commands available in your installed version.
In the terminal, flint help lists the starting points and flint <command> --help documents any single command. For the same catalog as data:
flint schema commands --output json
