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#

Shell
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:

Shell
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
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:

Shell
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.

Shell
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:

Shell
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:

Shell
flint payment-intents list --context ctx_123

Projects can pin a context in .flint/config.json:

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#

Shell
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:

Shell
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:

Shell
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:

Shell
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:

Shell
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:

Shell
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:

JSON
{
  "line_items": [{
    "name": "Design consultation",
    "quantity": 1,
    "unit_price_money": {"amount": 2500, "currency": "USD"}
  }],
  "tax": {"enabled": false}
}
Shell
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:

Shell
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:

Shell
flint listen --forward-to http://localhost:8080/webhooks/flint

It prints a signing secret on startup:

Text
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:

Shell
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:

Shell
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:

Shell
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 PATH prints one value, unquoted, for direct capture into a shell variable.
  • --select FIELDS narrows the output to specific fields.
  • --jq EXPR applies a jq expression to the result.
  • --all follows pagination and returns every page (see Pagination).
  • --idempotency-key KEY sets the idempotency key on a write.
  • --dry-run=client validates 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:

CodeMeaning
0Success
1The API returned an error
2Usage error (bad flag, missing argument)
3Authentication or configuration problem
4Confirmation required and not given
5Network or connectivity failure
6--wait-for timed out before the condition was met
70Internal 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:

Shell
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:

Shell
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:

JSON
{ "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:

Shell
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_:

Shell
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:

Shell
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:

Shell
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:

Shell
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.

Shell
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:

Shell
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:

Shell
flint schema commands --output json

Was this helpful?