The official Node.js library for the Solidgate API v2: customers, payment instruments, subscriptions, invoices, routing, reports, webhooks and every other resource of the API, fully typed for TypeScript.
- Typed requests, responses and webhook events, generated from the API Reference
- Automatic retries with backoff, and idempotency keys for safe retries of writes
- Auto-pagination with
for await - Webhook signature verification
- CommonJS and ES modules, no runtime dependencies
The payments API v1 (charge, refund, payment form) is a separate package,
@solidgate/node-sdk. You can install both.
- API Reference: every endpoint, field and error code
- Payment guide: products and flows
- Changelog: what changed in each version
- Node.js 22 or later
- A secret API key from Solidgate Hub: Developers v2 → API → Keys
npm install @solidgate/node-api-sdk
# or: pnpm add @solidgate/node-api-sdk / yarn add @solidgate/node-api-sdkimport { Solidgate } from '@solidgate/node-api-sdk';
// CommonJS: const { Solidgate } = require('@solidgate/node-api-sdk');
const solidgate = new Solidgate({ apiKey: process.env.SOLIDGATE_API_KEY! });
const customer = await solidgate.customers.createCustomer({
type: Solidgate.CreateCustomerRequestTypeEnum.Business,
email: 'jane@example.com',
});
console.log(customer.id);Every API group is a resource on the client (solidgate.customers, solidgate.subscriptions, solidgate.invoices,
solidgate.reports, …) with one method per endpoint, named after its operation in the API Reference. Request and
response models are on the Solidgate namespace, e.g. Solidgate.CustomerDomainModel.
const solidgate = new Solidgate({
apiKey: process.env.SOLIDGATE_API_KEY!,
timeout: 30_000,
maxRetries: 3,
appInfo: { name: 'my-shop', version: '1.4.0' },
});| Option | Default | Description |
|---|---|---|
apiKey |
required | Secret API key, sent as Authorization: Bearer <key> |
timeout |
60000 |
Milliseconds per attempt, including reading the response; at most 600000 |
maxRetries |
2 |
Retries after the first attempt (see Retries) |
channelId |
Added to every request that takes a channel_id and has none |
|
appInfo |
{ name, version?, url? } of your application, added to the User-Agent |
|
fetch |
global fetch |
Your own fetch, e.g. for a proxy or tests |
baseUrl |
https://api.solidgate.com/v2 |
Another API URL |
solidgate.withOptions({ timeout: 5_000 }) returns a copy of the client with some options changed (all but apiKey).
Every method takes the request as its first argument and options for this call only as its second:
const controller = new AbortController();
await solidgate.customers.getCustomer({ id: 'cus_123' }, { timeout: 10_000, maxRetries: 0, signal: controller.signal });| Option | Description |
|---|---|
timeout |
Milliseconds per attempt for this call |
maxRetries |
Retries for this call |
signal |
An AbortSignal to cancel the call, retries included |
idempotencyKey |
Your idempotency key, on endpoints that accept one (see below) |
List and search methods return a promise of the first page that is also an async iterable over every item:
// Every customer, page after page, fetched as you go
for await (const customer of solidgate.customers.listCustomers({})) {
console.log(customer.id);
}
// The first page only: items and the next page token
const page = await solidgate.customers.listCustomers({ pagination: { page_size: 50 } });
// At most 100 items, as an array
const first100 = await solidgate.customers.listCustomers({}).toArray({ limit: 100 });
// Page by page
for await (const page of solidgate.customers.listCustomers({}).pages()) {
console.log(page.items.length);
}Endpoints that accept an Idempotency-Key header get one automatically, generated once and reused on every retry, so
a retried write is never applied twice. Pass your own key to make a call safe to repeat later, e.g. after a restart:
const invoice = await solidgate.invoices.createInvoice(params, { idempotencyKey: `order-${order.id}` });
invoice.responseDetails.idempotency?.replayed; // true when the API returned the result of an earlier callReusing a key with a different request body throws Solidgate.IdempotencyKeyReusedError.
Failed calls are retried up to maxRetries times (default 2) with exponential backoff and jitter:
- Network errors: reads and writes with an idempotency key are retried; a write without one only when the request never reached the API.
429 Too Many Requests: retried after the wait the API asks for (up to 60 seconds).5xx: reads only. A write may already have been applied, so it fails with the error instead.
Set maxRetries: 0 to turn retries off.
Every result has a responseDetails property with the HTTP details of the response:
const customer = await solidgate.customers.getCustomer({ id: 'cus_123' });
customer.responseDetails.requestId; // include it when you contact Solidgate support
customer.responseDetails.statusCode; // 200
customer.responseDetails.headers.get('content-type');solidgate.reports.downloadReport() does not return the report file. It returns a short-lived download link
(url), and you download the file from it with a plain request, for example straight to disk:
import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
import { pipeline } from 'node:stream/promises';
import type { ReadableStream } from 'node:stream/web';
const { url } = await solidgate.reports.downloadReport({ id: report.id });
const response = await fetch(url); // the link needs no API key
await pipeline(Readable.fromWeb(response.body as ReadableStream), createWriteStream('report.csv'));The link expires soon after it is issued: download right away, or call downloadReport() again for a new one. The SDK
does not follow the link itself, so your API key is never sent to the file host.
Every error the SDK throws is a Solidgate.SolidgateError. API errors have status, code, message and
requestId; handle them by class:
try {
await solidgate.customers.getCustomer({ id: 'cus_123' });
} catch (error) {
if (error instanceof Solidgate.NotFoundError) {
// no such customer
} else if (error instanceof Solidgate.ValidationError) {
console.error(error.fieldErrors); // { email: { type, message }, ... }
} else if (error instanceof Solidgate.APIError) {
console.error(error.status, error.code, error.requestId);
} else {
throw error;
}
}| Class | When |
|---|---|
ValidationError |
400: invalid request; fieldErrors names each invalid field |
AuthenticationError |
401: missing or wrong API key |
PermissionDeniedError |
403: the key may not do this |
NotFoundError |
404 |
ConflictError |
409 |
StateError |
422: the object's state does not allow the operation |
IdempotencyKeyReusedError |
422: an idempotency key reused with a different body |
RateLimitError |
429, after retries; nextTryAt says when to try again |
InternalServerError |
5xx |
ConnectionError |
no response: network error |
TimeoutError |
no response in timeout (a ConnectionError) |
WebhookSignatureError |
a webhook that does not verify (see Webhooks) |
API errors extend Solidgate.APIError; all errors extend Solidgate.SolidgateError. The error codes are listed in the
API Reference.
constructEvent verifies the signature and returns the typed event. Pass the raw request body: a body that was parsed
and serialized again no longer matches its signature.
import express from 'express';
import { Solidgate } from '@solidgate/node-api-sdk';
const webhooks = new Solidgate.Webhooks({ secret: process.env.SOLIDGATE_WEBHOOK_SECRET! });
const app = express();
app.post('/webhooks/solidgate', express.raw({ type: 'application/json' }), async (req, res) => {
let event;
try {
event = await webhooks.constructEvent(req.body, req.headers);
} catch (error) {
return res.sendStatus(400); // wrong signature or body
}
if (webhooks.isKnownEvent(event)) {
switch (event.event_type) {
case 'INVOICE_PAID':
console.log(event.data.invoice.id); // typed for this event
break;
}
}
res.sendStatus(200);
});- Typed events:
isKnownEventnarrows the event, soevent.datahas the right type for eachevent_type. An event type newer than your SDK version comes back as anUnknownEventwith its raw data instead of an error. - Duplicates: a webhook may be delivered more than once. Deduplicate on the
solidgate-event-idheader (the same value asevent.event_id) and keep processed IDs for at least 7 days. - Secret rotation: pass both secrets while you switch:
new Solidgate.Webhooks({ secret: [newSecret, oldSecret] }). - Testing:
Solidgate.signPayload(body, secret)returns thesignatureheader for a body, so your tests can send signed webhooks.
The package ships its type declarations. Every request, response and event model is exported on the Solidgate
namespace:
import { Solidgate } from '@solidgate/node-api-sdk';
function describe(customer: Solidgate.CustomerDomainModel): string {
return `${customer.id}: ${customer.email}`;
}This package follows semantic versioning. While the version is 0.x, a breaking change raises
the minor version (0.2.0 → 0.3.0) and everything else the patch version, so ^0.2.0 never brings a breaking
change. From 1.0.0, breaking changes raise the major version. Every release is listed in the
changelog and on GitHub Releases.
Looking for help? Contact us. Include the requestId of the
failing call, from responseDetails.requestId or the error's requestId.
Found a bug in this library? Open a GitHub issue.