Node SDK
Use @flintpay/node when your integration runs in Node.js or TypeScript and you want typed request/response shapes, retry behavior, and async pagination helpers.
- npm package:
@flintpay/node - Source:
flint-pay/flint - Node requirement:
>=18
Install#
npm install @flintpay/node
Use the SDK from your server, worker, or other trusted backend environment. Do not ship your Flint API key in browser or mobile client code.
If you need to set up a Flint account before you have a normal API key, use the /v1/onboarding/... state machine and POST /v1/merchant-account-sessions over plain HTTP, as walked through in API & Agent Onboarding. The SDK does not currently wrap the onboarding state machine or the merchant-account-session mint; the rest of this guide assumes you already have an API key.
After setup, sandbox-management routes accept a normal external API key. The main Flint client exposes those routes under flint.developer.
What The SDK Wraps#
The SDK is a typed client over Flint's public contract. It maps clean TypeScript methods onto the supported external API surface, but it is not intended to expose every internal proto-only field or transport detail.
Use the SDK when you want:
- camelCase method params and response fields
- typed money objects, enums, and dates
- built-in async pagination
- consistent retry and error handling
Use raw HTTP when you are integrating from another language, need exact wire-level control, or want to work directly with the documented JSON payloads.
Initialize the Client#
import { Flint } from "@flintpay/node";
const flint = new Flint({
apiKey: process.env.FLINT_API_KEY!,
});
By default the SDK talks to https://api.withflintpay.com. Use a flint_test_... key for sandbox traffic and a flint_live_... key for live traffic.
For ongoing sandbox automation after setup:
const sandboxes = await flint.developer.listSandboxes();
For disposable sandbox automation, call resetSandbox(), capture the replacement sandboxId from the response, and issue new test keys for it. The pre-reset ID and keys belong to the archived environment.
If you need per-key request visibility for sandbox or CI traffic, issue a key with the developer.request_logs.self.read scope and read logs through flint.developerLogs.
First Calls#
Create an order, then create a payment intent from that order:
const order = await flint.orders.create({
lineItems: [
{
name: "Premium Widget",
quantity: 1,
unitPriceMoney: { amount: 2500, currency: "USD" },
},
],
});
const paymentIntent = await flint.paymentIntents.create({
orderId: order.orderId,
});
The payment intent amount is derived from the current order balance, the same way it is in the HTTP API.
Pagination#
List methods return an async iterable:
for await (const customer of flint.customers.list({ limit: 25 })) {
console.log(customer.customerId, customer.email);
}
The same pattern works for webhook events and API request logs:
for await (const event of flint.webhooks.listEvents({
webhookEndpointId: "whep_123",
status: "failed",
})) {
console.log(event.webhookEventId, event.lastError);
}
for await (const log of flint.developerLogs.list({
status: "client_error",
pathQuery: "/v1/orders",
})) {
console.log(log.requestId, log.statusCode, log.errorCode);
}
Inspect one webhook event and its delivery attempts with:
const event = await flint.webhooks.getEvent("whev_123");
const attempts = await flint.webhooks.listDeliveryAttempts(event.webhookEventId);
Headless Checkout#
For a merchant-owned checkout, the SDK can hold a checkout credential instead of an API key:
import { Flint } from "@flintpay/node";
const checkout = Flint.forCheckout({
checkoutSessionId: "cs_1kmn0aExample",
checkoutSessionSecret: session.checkoutAccess.checkoutAuthToken,
});
FlintConfig is a union: pass apiKey or checkoutSession, never both. Passing both throws. A checkout client sends the X-Checkout-Session-ID and X-Checkout-Session-Secret headers and is scoped to that session's own order, which is what keeps it from becoming a second API key.
Helpers for the collection flow live in the same package:
import {
stripeElementsBootstrap,
paymentLegSelections,
payOrderWithPaymentLegs,
pendingStripeActions,
checkoutProblemRemediations,
} from "@flintpay/node";
stripeElementsBootstrap(paymentCollection)turns Flint's collection guidance into the arguments forStripe(...)andstripe.elements(...), lowercasing the currency for you. It throws unlessnextStepiscreate_confirmation_token, so it is for checkout-authenticated collection; a merchant-authenticated read returnscollect_payment_sourceand should go throughpaymentSourcedirectly.paymentLegSelections(bindings)buildspaymentIntents[]for split tender, mixing newly collected ConfirmationTokens and saved payment methods. It rejects duplicate legs and empty credentials rather than letting the API do it.payOrderWithPaymentLegs(flint, orderId, expectedOutstandingMoney, bindings, options)is the multi-leg pay call.payOrderWithConfirmationTokensis the same function narrowed to token-only bindings.pendingStripeActions(attempt)flattenspendingActions[]into the values a browser needs for the named Stripe.js call.checkoutProblemRemediations(session)returns only the problems that carry a remediation, with whether each blocks completion.
Resume a frozen attempt with the ordinary pay method, passing the attempt ID and no start fields:
const result = await flint.orders.pay(orderId, {
paymentAttemptId: "opat_1kmn0aExample",
});
resumeOrderPayment() does not resume a payment despite its name. It reads the attempt through GetOrderPaymentAttempt. Use it to poll state, and use flint.orders.pay(orderId, { paymentAttemptId }) to actually resume.
A complete backend-for-frontend built on these helpers ships in the repo at sdks/node/examples/headless-checkout-bff.ts. Start there rather than assembling the calls yourself, and see Build your own checkout for the surrounding integration.
What The SDK Does Not Cover Yet#
Two public surfaces have no generated services, so call them over plain HTTP:
- Customer sessions (
POST /v1/customer-sessionsand its refresh and revoke routes). - The
/v1/menamespace, the buyer-scoped read and write surface behind a customer session.
A buyer account built on /v1/me therefore uses fetch today, even though the rest of your integration can use the SDK.
Errors#
SDK requests throw FlintError with Flint's public error metadata:
import { FlintError } from "@flintpay/node";
try {
await flint.orders.get("ord_missing");
} catch (error) {
if (error instanceof FlintError) {
console.error(error.type, error.code, error.message);
}
}
Retries apply automatically to GET requests and to write requests that are idempotency-backed in the public contract.
Next Steps#
- Accept Your First Payment for the same flow with raw HTTP
- CLI for the same API from a terminal or CI job, where no application code is involved
- Embedded Payments with Stripe Elements for payment collection in your own UI
- Build your own checkout for the merchant-owned checkout these helpers serve
- Idempotency for retry-safe write behavior
- Orders API Reference
- Payments API Reference
- Webhooks API Reference
- API Request Logs
