Pine Labs CLI

Official command-line interface for the Pine Labs Plural platform. Create orders, listen for and replay webhooks, run deterministic API calls, and automate workflows from your shell or CI.

The Pine Labs CLI (pinelabs) is the official command-line interface for the Pine Labs Plural platform. It is a thin, deterministic, fully auditable client over the Pine Labs Online APIs — the same APIs your production code calls.

Use it to:

  • Create and inspect orders, payments, refunds, payouts, subscriptions, and settlements.
  • Receive Pine Labs webhook events on a local URL over a secure WebSocket relay.
  • Sign, verify, and replay webhook events against your handlers.
  • Drive the same workflows from CI with structured JSON output.
  • Drive the same workflows through an MCP-backed ask command for assistive exploration.

Versioning. The CLI follows semver from 1.0.0. Breaking changes bump the major version with a deprecation cycle. Pin to an exact version in CI: npm install -g @pinelabs/cli@1.0.1.

This page is split in two:

  1. Get started fast — install, log in, first order, listen, trigger. Read this first if you have 2 minutes.
  2. CLI reference — flags, authentication, every command group, webhooks/events, MCP, security, troubleshooting. Deep dive.

Quickstart in 60 seconds

Bash
npm install -g @pinelabs/cli

export PINELABS_CLIENT_ID="your_client_id_here"
export PINELABS_CLIENT_SECRET="your_client_secret_here"

pinelabs login
pinelabs orders create --order-amount 100 --currency INR \
  --merchant-order-reference quickstart_$(date +%s)

On Windows (PowerShell), use $env:PINELABS_CLIENT_ID = "..." and $env:PINELABS_CLIENT_SECRET = "..." instead of export.

Four lines from a clean shell to a real sandbox order. If npm isn't an option, see Install for alternative install paths. The longer Get started fast section adds webhooks, replay, and CI patterns.


Common tasks

Developers think in tasks, not commands. The most common ones map directly:

TaskCommand
Create a test orderpinelabs orders create --order-amount 49900 --currency INR --merchant-order-reference order_$(date +%s)
Look up an orderpinelabs orders get ord_abc
Refund an orderpinelabs refunds create --order-id ord_abc --amount 10000
Listen for webhooks locallypinelabs listen --forward-to http://localhost:3000/webhooks
Replay failed deliveriespinelabs events replay-spool --forward-to http://localhost:3000/webhooks
Trigger a sandbox eventpinelabs trigger payment.success
Verify a webhook signaturepinelabs webhooks verify --webhook-id evt_x --webhook-timestamp 17… --webhook-signature … --body @payload.json
Check a settlement by UTRpinelabs settlements get-by-utr <utr>
Confirm which environment you're onpinelabs whoami
Self-diagnose a broken setuppinelabs doctor

Each row is documented in full in the CLI reference.


How the CLI works (mental model)

The CLI is intentionally a thin layer. Holding the right mental model makes every flag below obvious:

  • pinelabs <resource> calls Pine Labs APIs directly. No proxy, no local rewriting — the CLI is a faithful HTTP client with authentication, idempotency keys, and audit logging bolted on.
  • pinelabs listen opens an outbound WebSocket to the Pine Labs relay and forwards events to your local URL. There is no inbound port, public tunnel, or localhost exposure.
  • State on disk is small and well-known: ~/.pinelabs/config.json (credentials, profiles), ~/.pinelabs/audit.log (every command + request id), and ~/.pinelabs/spool.jsonl (failed-delivery queue). Delete the directory and the CLI is reset.
  • Replay is real, not mocked. events replay-spool re-POSTs spool entries with the original body and webhook-id, but with a freshly signed webhook-timestamp / webhook-signature so spec-compliant verifiers accept them. Your handler runs against signatures it would see in production.

Sandbox vs production

The CLI talks to whichever environment your active profile points at. There is no --live / --mode flag yet; the active api_base_url is the source of truth. Always verify before any destructive command:

Bash
pinelabs whoami
# look at "api_base_url"
#   sandbox    → https://pluraluat.v2.pinepg.in/...
#   production → https://api.pluralpay.in/... (or your tenanted host)

A safer pattern is to keep separate named profiles and let the profile name carry the environment:

Bash
pinelabs --profile sandbox login
pinelabs --profile prod    login

pinelabs --profile sandbox orders create …   # daily testing
pinelabs --profile prod    orders get  ord_x  # read-only investigation

Warning. Never run pinelabs trigger against a production profile unless you fully understand the downstream side effects — see the warning under trigger.


Why Pine Labs CLI is different

  • Deterministic workflows. Every write carries an Idempotency-Key so retries are safe; the documented per-endpoint contract tells you exactly what replays vs. dedups vs. surfaces.
  • Signature-faithful webhook replay. Failed deliveries land in a local spool, are re-signed with a fresh timestamp on replay, and pass spec-compliant 5-minute tolerance verifiers. No "strip signatures to make it work" hacks.
  • Audit log by default. Every command, request id, and response status goes to ~/.pinelabs/audit.log with PCI/PII redacted before write — the artifact Support actually asks for.
  • AI-assisted with guardrails. The MCP-backed ask planner proposes tool calls; the CLI validates, derives idempotency keys deterministically, and prompts before any non-readonly action. The planner cannot run payments directly.
  • jq-safe output. Strict stdout/stderr split means prompts, warnings, and progress lines never leak into machine-readable JSON.

Idempotency behaviour by endpoint

The CLI sends an Idempotency-Key header on every write, but the server handles it differently per endpoint. Understand the contract before you retry:

EndpointDedupe primitiveRetry behaviourWhat the CLI does
POST /ordersmerchant_order_referenceReturns HTTP 422 DUPLICATE_REQUEST on conflict — not a replay of the original response.Auto-generates a unique reference per invocation. If you pass --idempotency-key, it is also used as the reference so header and body agree.
POST /refundsmerchant_order_referenceReplays the original 2xx response on retry. Safe to retry on transport errors.Auto-generates a key per invocation; override with --idempotency-key.
POST /payoutsclientReferenceIdReplays the original 2xx response on retry.Same as refunds.
raw POST …Idempotency-Key headerDepends on the underlying endpoint.Sends the key you pass via --idempotency-key, or auto-generates one.

Note. The CLI retries on 502, 503, and 504 (transport/gateway errors). A 500 is surfaced immediately because the server may have already committed the side effect.

If you've been writing payments code with curl, Postman collections, or generic HTTP clients, this is what you're trading them for.

How it compares

CapabilityPine Labs CLIcurl / PostmanGeneric CLIs
Native auth + token refreshvaries
Deterministic idempotency keys per writemanualpartial
Webhook relay over outbound WSS (no tunnels)partial
On-disk spool with signature-faithful replaypartial
Audit log with PCI/PII redactionpartial
AI-assisted (ask) with deterministic guardrails
Strict stdout/stderr split (… | jq always works)n/avaries
First-class Pine Labs Plural endpointsmanual

The table is qualitative — individual tools differ — but the design delta is real: this CLI treats reliability primitives (idempotency, audit, replay) as defaults, not opt-ins.


When to use the CLI — and when not to

Use it for:

  • Building and testing integrations against the Plural sandbox.
  • CI smoke tests and release verification (pinelabs orders create | jq -e).
  • Debugging live integration issues — audit log, whoami, doctor.
  • Replaying webhooks against your handler under realistic signatures.
  • Ad-hoc operator tasks (refund a known order, check a settlement UTR).

Do NOT use it for:

  • End-user authentication flows. The CLI uses OAuth2 client credentials — it is not an end-user login mechanism, has no browser/device-code flow, and is not designed to run in a customer's browser or app.
  • High-frequency production traffic. The CLI is a thin client over HTTP with sequential semantics; production payment volume should go through your own server-to-server integration of the Plural APIs, not through shelling out to a binary.
  • Storing long-lived credentials on shared / multi-user machines. ~/.pinelabs/config.json is 0600; that's enough for a single-user workstation or ephemeral CI runner, not a shared bastion host.

Get started fast

Install

The CLI ships pre-built native binaries for macOS, Linux, and Windows via npm. No build tools, compilers, or runtime dependencies required.

Bash
# Recommended (any OS with Node.js 16+)
npm install -g @pinelabs/cli

# Zero-install (one-off commands and CI)
npx @pinelabs/cli version

# Yarn
yarn global add @pinelabs/cli

# pnpm
pnpm add -g @pinelabs/cli

The package downloads the correct binary for your platform and architecture automatically.

Supported platforms:

PlatformArchitectures
macOSApple Silicon (arm64), Intel (x64)
Linuxx64, arm64
Windowsx64

Verify:

Bash
pinelabs version
# → pinelabs 1.0.1 (abc1234, built 2026-05-12T10:00:00Z)

Log in

The CLI authenticates with OAuth2 client credentials — the server-to-server flow. It is not designed for user login flows (there is no browser-based or device-code OAuth flow), so do not embed it in customer-facing sign-in paths.

You'll need your Client ID and Client Secret from the Pine Labs merchant dashboard (Developers → API Keys).

Bash
export PINELABS_CLIENT_ID="your_client_id_here"
export PINELABS_CLIENT_SECRET="your_client_secret_here"
pinelabs login

Expected output:

Text
logged in. Token preview: eyJhbG…****

OAuth2 Client Credentials. The CLI uses OAuth2 client credentials. There is no browser-based or device-code flow today; this auth model is intended for server-to-server integrations and CI runners, not interactive end-user sign-in. See Authentication for details.

By default login performs a token exchange against the Plural API and persists only the short-lived access_token (and expires_at). The client secret is read from the environment, used in memory, and never written to disk. See Authentication for --direct, --store-secret, and CI patterns.

First order

Bash
pinelabs orders create \
  --order-amount 49900 \
  --currency INR \
  --merchant-order-reference order_$(date +%s) \
  --customer-email jane@example.com

Sample response (JSON, abbreviated):

JSON
{
  "order_id": "ord_NQ4j8xK2pZmL1a",
  "status": "CREATED",
  "order_amount": {
    "value": 49900,
    "currency": "INR"
  },
  "merchant_order_reference": "order_1717420000",
  "redirect_url": "https://pay.pluralonline.com/p/NQ4j8xK2pZmL1a"
}

Note that order_amount is a nested object ({ value, currency }), not a top-level amount/currency pair — pipe accordingly:

Bash
pinelabs orders create ... | jq '.order_id, .order_amount.value'

Add --open to launch the redirect URL in your default browser.

Listen for webhooks

Bash
# one-time: store the webhook secret from your Pine Labs dashboard
pinelabs config set webhook_secret "your_webhook_secret_here"

pinelabs listen --forward-to http://localhost:3000/webhooks

Expected output:

Text
relay connected -> forwarding to http://localhost:3000/webhooks (verify=true)
payment.authorized evt_abc123 -> 200 (delivered)

The CLI dials a secure outbound WebSocket to the Pine Labs relay and forwards matching events to your local URL. listen verifies signatures by default and exits non-zero if no webhook_secret is configured — pass --skip-verify for local dev only. See listen for the full flag reference.

Trigger an event

Bash
pinelabs trigger payment.success
pinelabs trigger --list                       # see available fixtures
pinelabs trigger payment.success --add amount.value=15000

Triggered events are dispatched server-side; they flow back through the same relay and Pine Labs signature pipeline used by listen. --add overrides and --raw payloads are forwarded as the event body, so an active pinelabs listen will see exactly the payload you constructed.


CLI reference

Global flags

These flags are registered on the root command and are inherited by every subcommand:

FlagDefaultDescription
--profile <name>defaultConfig profile to use (see Profiles).
--api-base <url>profile valueOverride the active profile's api_base_url for this invocation only. Does not persist.
--timeout <duration>30sOverride the per-request HTTP timeout, for example 60s or 2m.

Subcommands add their own flags — see each command. Common subcommand flags include --idempotency-key, --yes, and resource-specific identifiers. --help is available on every command.

Environment variables

The CLI is conservative about implicit configuration. Every Pine Labs or LLM-specific environment variable it reads directly is listed below. Standard OS/runtime variables such as HOME and HTTP proxy settings may still affect file locations or network transport.

Authentication

VariableUsed byDescription
PINELABS_CLIENT_IDloginOAuth client id (required for login).
PINELABS_CLIENT_SECRETlogin; runtime by every command in two cases(a) --direct profile without --store-secret, or (b) OAuth profile whose access token has expired — the CLI does not store a refresh token, so it must re-exchange client_id + client_secret to mint a fresh bearer. Keep this exported in any shell that runs CLI commands beyond the access-token lifetime, or accept a one-time pinelabs login to re-mint. Never persisted unless --store-secret is passed.

Webhooks

VariableUsed byDescription
PINELABS_WEBHOOK_SECRETwebhooks verify, webhooks signDefault signing secret when --secret is omitted. Base64-encoded.

MCP relay (opt-in, AI workflows only)

VariableUsed byDescription
PINELABS_MCP_ENDPOINTmcp, askURL of the MCP server.
PINELABS_MCP_CLIENT_IDmcp, askMCP OAuth client id (falls back to PINELABS_CLIENT_ID).
PINELABS_MCP_CLIENT_SECRETmcp, askMCP OAuth client secret (falls back to PINELABS_CLIENT_SECRET).
PINELABS_MCP_API_KEYmcp, askStatic API-key auth, used when client credentials are absent.

ask planner (LLM selection)

VariableUsed byDescription
PINELABS_ASK_PROVIDERaskOverride LLM provider (openai, anthropic, groq, ollama).
PINELABS_ASK_MODELaskOverride model name.
PINELABS_ASK_MAX_TOOLSaskCap on the number of candidate tools sent to the planner per question.
PINELABS_ASK_DEBUGaskWhen set to 1/true, prints planner trace to stderr.
OPENAI_API_KEYaskAPI key for the OpenAI provider.
ANTHROPIC_API_KEYaskAPI key for the Anthropic provider.
GROQ_API_KEYaskAPI key for the Groq provider.
OLLAMA_HOSTaskBase URL for a local Ollama instance.

Other settings (API base URL, relay URL, merchant id, webhook secret, client id/secret) live in ~/.pinelabs/config.json and are managed via pinelabs config set.

Authentication

The CLI supports two credential paths.

1. OAuth client credentials (default, recommended)

Bash
export PINELABS_CLIENT_ID="your_client_id_here"
export PINELABS_CLIENT_SECRET="your_client_secret_here"
pinelabs login

This performs a POST to the Plural token endpoint and stores only the resulting short-lived access_token plus expires_at in ~/.pinelabs/config.json (mode 0600). The client secret is never written to disk in this mode.

2. Direct header auth (X-Client-Id / X-Client-Secret)

For environments that prefer header-based auth and want to skip token exchange:

Bash
pinelabs login --direct                # stores client_id only
pinelabs login --direct --store-secret # ALSO persists client_secret (CI only)

Without --store-secret, the client secret must be re-exported in every shell that runs the CLI. With --store-secret, the secret is written in plaintext to ~/.pinelabs/config.json (mode 0600) and the CLI prints a loud warning. Use this only on ephemeral CI runners.

Logout

Bash
pinelabs logout                  # clears tokens for the active profile
pinelabs logout --profile staging

Clears access_token, refresh_token, and expires_at but preserves client_id so pinelabs login can resume without re-entering credentials.

Profiles

The CLI supports multiple named profiles in a single config file.

Bash
pinelabs --profile staging login
pinelabs --profile staging orders create ...

Profiles are isolated: each has its own api_base_url, relay_url, client_id/client_secret, tokens, merchant_id, and webhook_secret.

Configuration

Config lives at ~/.pinelabs/config.json (mode 0600, parent dir 0700). The format is JSON, not TOML.

JSON
{
  "profiles": {
    "default": {
      "api_base_url": "https://pluraluat.v2.pinepg.in/api/pay/v1",
      "relay_url": "wss://relay.pinelabs.com/v1/listen",
      "auth_mode": "oauth",
      "client_id": "...",
      "access_token": "...",
      "expires_at": 1746630000,
      "merchant_id": "...",
      "webhook_secret": "..."
    }
  }
}

Manage it through the CLI (recommended) or hand-edit:

  • pinelabs config get <key>
  • pinelabs config set <key> <value>
  • pinelabs config list

Editable keys: api_base_url, relay_url, client_id, client_secret, merchant_id, webhook_secret. The session quartet (access_token, refresh_token, expires_at, auth_mode) is owned by login/logout and cannot be set directly.

Command groups

Every command supports --help. The lists below reflect what is wired up in the current CLI and in the generated command reference.

orders

Manage checkout orders — the top-level container for one or more payment attempts.

CommandEndpointDescription
pinelabs orders createPOST /api/checkout/v1/ordersCreate an order with redirect URL. Flags: --order-amount, --currency, --merchant-order-reference, --mode, --callback-url, --customer-email, --customer-mobile, --open, --idempotency-key
pinelabs orders get <id>GET /orders/{id}Fetch order details
pinelabs orders capturePOST /orders/{id}/captureCapture a pre-authorized order. Flags: --order-id, --merchant-capture-reference, --capture-amount (0 = full capture), --currency, --idempotency-key
pinelabs orders cancelPOST /orders/{id}/cancelCancel an order. Flags: --order-id, --yes, --idempotency-key
Bash
pinelabs orders create \
  --order-amount 49900 --currency INR \
  --merchant-order-reference order_42 \
  --callback-url https://example.com/return

payments

CommandEndpointDescription
pinelabs payments listGET /orders/{id}List payments embedded in an order. Flags: --order-id
pinelabs payments upiPOST /orders/{id}/paymentsUPI Collect/Intent flow. Flags: --order-id, `--txn-mode INTENT

Card payments and OTP flows are not yet first-class payment commands; use pinelabs raw against the corresponding endpoints.

refunds

CommandEndpointDescription
pinelabs refunds createPOST /refunds/{order-id}Refund an order (refunds are scoped to orders, not payments). Flags: --order-id, --amount, --currency, --merchant-order-reference (auto-generated if blank), --yes, --idempotency-key
Bash
pinelabs refunds create --order-id ord_abc --amount 10000

Refunds prompt for confirmation by default. Pass --yes in scripts.

payouts

CommandEndpointDescription
pinelabs payouts createPOST /payoutsCreate a payout (HIGH RISK). Flags: --amount, --currency, --beneficiary-id, `--mode IMPS
pinelabs payouts cancel <id>POST /payouts/{id}/cancelCancel a payout. Flags: --yes, --idempotency-key
pinelabs payouts get <id>GET /payouts/{id}Fetch payout details
pinelabs payouts balanceGET /payouts/balanceCheck payout balance

subscriptions and plans

CommandEndpointDescription
pinelabs subscriptions plans createPOST /subscriptions/plansCreate a plan. Flags: --name, `--frequency Day
pinelabs subscriptions plans listGET /subscriptions/plansList plans. Flags: --page, --size
pinelabs subscriptions plans delete <id>DELETE /subscriptions/plans/{id}Delete a plan (HIGH RISK). Flags: --yes, --idempotency-key
pinelabs subscriptions createPOST /subscriptionsCreate a subscription. Flags: --plan-id, --customer-id, --reference, --idempotency-key
pinelabs subscriptions get <id>GET /subscriptions/{id}Fetch subscription details
pinelabs subscriptions cancel <id>POST /subscriptions/{id}/cancelCancel a subscription (HIGH RISK). Flags: --yes, --idempotency-key
pinelabs subscriptions presentations createPOST /subscriptions/{id}/presentationsCreate a presentation (HIGH RISK). Flags: --subscription-id, --due-date, --amount, --currency, --reference, --yes, --idempotency-key
pinelabs subscriptions presentations get <id>GET /presentations/{id}Fetch presentation
pinelabs subscriptions presentations retry <id>POST /presentations/{id}/retryRetry a presentation (HIGH RISK). Flags: --yes, --idempotency-key
pinelabs subscriptions presentations delete <id>DELETE /presentations/{id}Delete a presentation (HIGH RISK). Flags: --yes, --idempotency-key

settlements

CommandEndpointDescription
pinelabs settlements get-by-utr <utr>GET /settlements/utr/{utr}Fetch settlement by UTR number

analytics

CommandEndpointDescription
pinelabs analytics success-rateGET /analytics/success-rateGet payment success rate. Flags: --since (default 24h, max 168h)

search

CommandEndpointDescription
pinelabs search transaction <id>GET /transactions/{id}Search for a transaction

Diagnostics: whoami and doctor

pinelabs whoami — Print the active profile's identity as JSON. Includes profile, api_base_url, relay_url, auth_mode, masked client_id and access_token, token_expired, webhook_secret_set, merchant_id, and logged_out status. Read-only, no API call.

pinelabs doctor — Run self-checks and print PASS/WARN/FAIL with an actionable fix string. Exits non-zero if any check fails so CI can gate on it. Flags: --json for machine-readable output.

Text
$ pinelabs doctor
[PASS] config.path             ~/.pinelabs/config.json
[PASS] config.mode             0600
[PASS] auth.state              oauth (bearer token present)
[PASS] auth.token              valid (expires in 47m12s)
[WARN] webhook.secret          no webhook_secret on profile
       fix: `pinelabs config set webhook_secret <base64>` …
[PASS] api.reachable           https://pluraluat.v2.pinepg.in/api/pay/v1 → HTTP 404
[PASS] relay.reachable         TLS handshake ok (relay.pinelabs.com:443)

7 check(s): 0 failed, 1 warned

raw — escape hatch

For endpoints that don't yet have a first-class command, raw lets you call any path with the active profile's auth applied:

Bash
pinelabs raw GET  /orders/ord_abc
pinelabs raw POST /refunds/ord_abc --data '{"order_amount":{"value":10000,"currency":"INR"}}'
pinelabs raw PUT  /subscriptions/plans/plan_xyz --data @plan.json

Webhooks

The webhooks group covers signature operations and event-type discovery. For receiving live events on localhost, use listen.

CommandDescription
pinelabs webhooks verifyVerify a webhook signature. Flags: --secret, --file/--body/stdin, --webhook-id, --webhook-timestamp, --webhook-signature, --now
pinelabs webhooks signProduce signed headers for a payload. Flags: --secret, --webhook-id, --webhook-timestamp, --body
pinelabs webhooks eventsList supported event types. Flags: --prefix, --json

pinelabs webhooks list and pinelabs webhooks get (endpoint management) are not implemented today.

listen

Open a secure WebSocket to the Pine Labs relay and forward events to a local URL.

Bash
pinelabs listen --forward-to http://localhost:3000/webhooks
FlagDefaultDescription
--forward-to <url>requiredLocal URL to POST events to.
--events <list>all eventsComma-separated event-type filter. Trailing .* is supported.
--skip-verifyfalseSkip signature verification (development only).
--header, -H k:vExtra headers to add on every forwarded request (repeatable).
--print-secretfalsePrint the signing secret on connect, then exit.
--print-jsonfalseMirror each accepted event as one JSON line on stdout.

Signature scheme. Pine Labs webhooks use three headers — webhook-id, webhook-timestamp, webhook-signature — with HMAC-SHA256 over id.timestamp.body and a base64-encoded secret.

Fail-fast on missing secret. If signature verification is enabled (the default) and no signing secret is available, listen exits non-zero immediately with a remediation message.

Reliability. Failed deliveries are written to a local on-disk spool (see Events). On pinelabs events replay-spool:

  • Body and webhook-id are preserved verbatim.
  • When webhook_secret is configured, replay re-signs with the current timestamp (old timestamps are rejected by the 5-minute tolerance window).
  • Without webhook_secret, original headers are forwarded verbatim.
  • Sends at most one copy per event_id per invocation (duplicates pruned before any POST).

trigger

Synthesize a webhook event by name. Triggers run server-side and the event flows back through the relay.

Bash
pinelabs trigger payment.success
pinelabs trigger --list                              # available fixtures
pinelabs trigger payment.success --add amount.value=15000
pinelabs trigger payment.success --raw '{"id":"evt_x","data":{"order_id":"ord_abc"}}'
FlagDefaultDescription
--listfalseList available fixtures and exit.
--add k=vAdd or override a field on the fixture. Dotted keys nest. Repeatable.
--raw <json>Replace the entire payload with this JSON (advanced).

Use sandbox only. pinelabs trigger calls the configured API's /events/trigger endpoint. Depending on the sandbox/backend fixture, that endpoint may create real sandbox transactions, refunds, or other objects. Never run against a live/production profile unless you fully understand the downstream side effects. Confirm pinelabs whoami shows a sandbox base URL first.

Events (replay)

Failed forward attempts during listen are appended to a local spool at ~/.pinelabs/spool.jsonl (JSON Lines, de-duplicated by event_id).

CommandDescription
pinelabs events resend <event-id>Ask Pine Labs to re-deliver a previously-sent event
pinelabs events replay-spoolRe-POST spool entries to a URL with re-signed headers. Flags: --forward-to <url>

Logs

Every CLI invocation appends a structured entry to ~/.pinelabs/audit.log. Bearer tokens, client secrets, card numbers, CVVs, and other PCI/PII are redacted before the entry is written.

CommandDescription
pinelabs logs tailFollow the local audit log. Flags: --remote, --method, --interval
pinelabs logs pathPrint the audit log path
pinelabs logs showPrint the last 50 audit entries

This is the artifact Pine Labs Support will ask for when triaging an integration issue.

MCP

Advanced feature for AI-assisted workflows. Optional — every command group above works without it.

The CLI ships an MCP (Model Context Protocol) JSON-RPC client for tooling integrations.

CommandDescription
pinelabs mcp pingHealth-check the configured MCP server
pinelabs mcp toolsList tools exposed by the MCP server
pinelabs mcp call <tool-name>Invoke a tool. Flags: --input, --yes

ask (AI-assisted, advanced)

Bash
pinelabs ask "find the most recent failed payment for order ord_abc"
FlagDefaultDescription
--planfalseShow the planned tool calls without executing them.
--yesfalseSkip confirmation for non-readonly tools (CI use).

Confirmation required. The planner proposes tool calls; the CLI validates and you confirm before any non-readonly tool runs. Idempotency keys for write operations are deterministically derived so plans are safely re-runnable. Refunds, payouts, and subscription cancellation always require confirmation.

Output format

The CLI follows a strict stdout/stderr split:

  • Resource commands emit pretty-printed JSON on stdout.
  • Status / progress commands print a short status line to stderr.
  • Confirmation prompts and warnings go to stderr.

This means … | jq is always safe on resource commands:

Bash
pinelabs orders create ... | jq '.order_id'
pinelabs refunds create --yes ... | jq '.refund_id'
pinelabs payments list --order-id ord_abc | jq '.[0]'

There is no --output/-o flag and no table/yaml/csv formatter today.

CI usage

YAML
- name: Smoke test against Pine Labs sandbox
  env:
    PINELABS_CLIENT_ID:     ${{ secrets.PINELABS_CLIENT_ID }}
    PINELABS_CLIENT_SECRET: ${{ secrets.PINELABS_CLIENT_SECRET }}
    PINELABS_CLI_VERSION:   1.0.1
  run: |
    npm install -g @pinelabs/cli@${PINELABS_CLI_VERSION}
    pinelabs login
    pinelabs orders create \
      --order-amount 100 --currency INR \
      --merchant-order-reference ci_${{ github.run_id }} \
      --idempotency-key ci_${{ github.run_id }} | jq -e '.order_id'

Alternatively, use npx for zero-install CI:

YAML
- name: Smoke test (npx, no install step)
  env:
    PINELABS_CLIENT_ID:     ${{ secrets.PINELABS_CLIENT_ID }}
    PINELABS_CLIENT_SECRET: ${{ secrets.PINELABS_CLIENT_SECRET }}
  run: |
    npx @pinelabs/cli@1.0.1 login
    npx @pinelabs/cli@1.0.1 orders create \
      --order-amount 100 --currency INR \
      --merchant-order-reference ci_${{ github.run_id }} \
      --idempotency-key ci_${{ github.run_id }} | jq -e '.order_id'

For non-interactive runs:

  • Pass --yes to skip confirmation prompts on destructive commands.
  • Pass --idempotency-key explicitly so retries are safe.
  • Pin a specific version (@1.0.1) so unexpected releases don't break your pipeline.
  • Log pinelabs version with every run for an audit trail.

Security model

  • Credentials. OAuth client_secret is never persisted by default; only a short-lived access_token is. --direct mode stores the client_id only; --direct --store-secret is opt-in and warns loudly.
  • File modes. ~/.pinelabs/config.json is 0600; the parent ~/.pinelabs/ is 0700. Writes are atomic (tmp + rename).
  • Secret redaction. The audit log masks bearer tokens, client secrets, card numbers, CVVs, and other PCI/PII before write.
  • Egress-only network. The relay is outbound WSS — no inbound ports, no public tunnels, no localhost exposure.
  • Idempotency. Write operations carry an Idempotency-Key. The MCP executor derives keys deterministically from tool inputs.
  • Never commit your config directory. Add ~/.pinelabs/ to .gitignore:
Text
# Pine Labs CLI local state — never commit
.pinelabs/

Found a vulnerability? Email security@pinelabs.com.

Troubleshooting

Debug checklist

When something looks wrong, run these four in order:

  1. pinelabs whoami — confirms the active profile, masked credentials, and environment.
  2. pinelabs doctor — 7-check self-diagnosis. Exits non-zero on failure.
  3. pinelabs config get webhook_secret — confirms the signing secret matches your dashboard.
  4. pinelabs logs tail — follow the local audit log.

pinelabs login fails with "set PINELABS_CLIENT_ID and PINELABS_CLIENT_SECRET"

Both env vars are required. Export them in the same shell:

Bash
export PINELABS_CLIENT_ID="your_client_id_here"
export PINELABS_CLIENT_SECRET="your_client_secret_here"
pinelabs login

token exchange failed on login

Your account may not support OAuth token exchange. Fall back to direct header auth:

Bash
pinelabs login --direct

Subsequent commands fail with auth errors after --direct

Without --store-secret, the secret is not on disk. Re-export PINELABS_CLIENT_SECRET in your shell, or re-run pinelabs login --direct --store-secret (CI use only).

listen drops events with signature errors

The webhook secret stored on the profile doesn't match what the relay is using. For local dev only, pass --skip-verify. In real environments:

Bash
pinelabs config set webhook_secret "your_webhook_secret_here"

Replay didn't drain my spool

pinelabs events replay-spool --forward-to ... only acknowledges entries that get a 2xx from your handler. Check ~/.pinelabs/spool.jsonl and pinelabs logs tail to see why entries are failing.

Limitations and roadmap

The Pine Labs CLI follows semver from 1.0.0. The underlying Plural APIs are stable; the deterministic command paths, webhook relay/replay flow, audit trail, and MCP guardrails are built for real integration workflows.

Versioning:

  • Plural APIs: stable, governed by the upstream API versioning policy.
  • CLI surface: semver from 1.0.0. Breaking changes bump the major version. Pin to a specific version in CI.
  • Audit log / spool format: considered internal, may change between minor versions.

What ships today:

  • Pre-built native binaries (macOS, Linux, Windows) via npm
  • Orders, payments, refunds, payouts, subscriptions, settlements
  • Webhook relay with signature verification, spool, and replay
  • OAuth + direct-header auth with profile isolation
  • Audit log with PCI/PII redaction
  • Shell completion via pinelabs completion bash|zsh|fish|powershell
  • MCP-backed ask with deterministic guardrules
  • raw escape hatch for any Plural endpoint

What's next:

Upcoming work includes output formatting (--output table|csv), --verbose/--debug flags, and additional install channels (Homebrew, Docker). Email developer-tools@pinelabs.com to influence priority.

Resources

License

Apache 2.0.

New chat
Responses are generated using AI and may contain mistakes.
Hi! I'm Pine, your AI developer assistant. Ask me anything about Pine Labs APIs, integrations, or troubleshooting.

Tip: you can create a new chat with + E