SDKs

Flint publishes two official server SDKs, both generated from Flint's public OpenAPI contract and released from flint-pay/flint-sdks. They cover all 497 operations at API version 2026-09-07, and they send that version on every request.

PackageInstallRuntime
@flintpay/nodenpm install @flintpay/nodeNode.js 22+, TypeScript 5.9+, ESM
flintpay/flintcomposer require flintpay/flintPHP 8.2+ with the cURL and JSON extensions

Every request carries a credential, so both packages belong on a server, a worker, or another trusted backend. A Flint API key never goes in browser or mobile code.

Neither is required. Every guide here shows the HTTP request, and the CLI drives the same API from a terminal or a CI job.

Create a client#

A client needs baseUrl, an authMode, and the credentials for that mode. It reads no environment variables and discovers no credentials: whatever you pass is what it sends.

TypeScript
import { Client } from "@flintpay/node";

const flint = new Client({
  baseUrl: "https://api.withflintpay.com",
  authMode: "merchant",
  credentials: {
    merchant: { BearerAuth: process.env.FLINT_API_KEY! },
  },
});

The key decides the environment: a flint_test_... key runs against your sandbox and a flint_live_... key runs against your live account. Constructing a client performs no network I/O.

Authentication modes#

Set authMode and the matching credentials entry on the client, or per request. The mode has to be one the operation accepts.

  • merchant carries your API key as a bearer token under BearerAuth. Most operations use it.
  • merchantKey carries the same key as X-API-Key, under ApiKeyHeader.
  • customer carries a customer session under CustomerSessionBearer, for the /v1/me routes.
  • checkout carries a checkout session's ID and secret under CheckoutSessionIDHeader and CheckoutSessionSecretHeader, scoped to that session's own order.
  • invoice carries a buyer's invoice access token under InvoiceAccessTokenBearer.
  • onboarding carries an onboarding session under OnboardingSessionBearer, for the routes you call before an API key exists.

Send one mode per request. Combining checkout credentials with Authorization returns 400 AMBIGUOUS_AUTH.

Make a call#

Methods live on client.api and are named for the operation IDs in the OpenAPI contract. A JSON request body goes under body; path parameters, query parameters, and headers sit beside it as siblings.

TypeScript
const result = await flint.api.createOrder({
  "Idempotency-Key": "order-2026-09-10-0001",
  body: {
    line_items: [
      {
        name: "Premium Widget",
        quantity: "1",
        unit_price_money: { amount: "2500", currency: "USD" },
      },
    ],
  },
});

const order = result.data.data;
console.log(order.order_id, result.meta.requestId);

The generated reference lists every operation with its input and response types, for Node and for PHP. Each package also ships a runnable example per operation.

Field names and money#

Inputs use the API's wire names, so unit_price_money, not unitPriceMoney.

Exact numbers are strings. Amounts, quantities, and 64-bit integers are "2500" rather than 2500, which keeps them out of floating point on the way to the wire. They are still integers in the smallest currency unit, so "2500" is $25.00. Money & currency covers the model.

Results and errors#

Every call resolves to a result with data, meta, and the raw response. data is the parsed response body, so a resource sits at result.data.data and the response's own metadata sits beside it. Node exposes result metadata as properties (result.meta.requestId); PHP uses array keys ($result->meta['requestId']). PDF routes return Uint8Array in Node and binary-safe strings in PHP.

Failures throw SdkError with kind, outcome, and retryAllowed. outcome is one of:

  • not_sent: the request never left the client, so nothing changed.
  • response: Flint answered, and the error describes that answer.
  • unknown: the request may have reached Flint. On a write, reconcile against the resource and your stored idempotency key before resubmitting rather than sending it again.

Error handling covers the API error codes behind these exceptions.

Request options#

The second argument to any method carries per-request settings: a plain object in Node, a RequestOptions in PHP.

timeoutMsnumber

Bounds one attempt, including reading the body. Defaults to 10000.

deadlineMsnumber

Bounds the whole call, including waits. Defaults to 30000. It is a duration, not a timestamp.

authModestring

Selects a mode for this request alone, with credentials supplying the values.

headersobject

Per-request headers. They stay local to the request rather than mutating client state.

signalAbortSignal

Node only. PHP uses a Cancellation token checked during cURL progress. Canceling stops local work, not the remote operation.

Idempotency and retries#

The SDKs send each request once and never generate an idempotency key for you. Retrying is yours to do.

Send Idempotency-Key in the input object, alongside the path and query parameters, and persist it before the first attempt so the retry after a lost response reuses it:

TypeScript
await flint.api.createOrder({
  "Idempotency-Key": storedKey,
  body: { line_items: [/* … */] },
});

Idempotency covers how to choose keys and how long Flint honors them. Disable retries in any transport or HTTP library you wrap around the SDK, so one logical attempt does not become several.

Pagination#

List routes return one page. Read next_page_token from the response and send it back as page_token until it comes back empty:

TypeScript
let pageToken: string | undefined;

do {
  const result = await flint.api.listOrders({ page_size: 100, page_token: pageToken });
  for (const order of result.data.data) {
    console.log(order.order_id, order.status);
  }
  pageToken = result.data.next_page_token;
} while (pageToken);

Use each token once and in order. Pagination covers sorting, filtering, and what a cursor holds.

Webhook verification#

The client verifies a signature against the raw request bytes:

TypeScript
const { event } = flint.verifyWebhook(rawBody, headers, [
  process.env.FLINT_WEBHOOK_SECRET!,
]);

It checks the webhook-id, webhook-timestamp, and webhook-signature headers with a 300 second tolerance, and it accepts a list of secrets so you can rotate one without dropping deliveries. Pass the bytes your server received: verifying reserialized JSON fails.

Verification is not deduplication. Webhooks covers the delivery guarantees and the webhook_event_id you store to make handling idempotent.

Next steps#

Rate this doc