smsprosdk
v0.2.3
Published
Typed JavaScript/TypeScript SDK for SMS Pro platforms — manage contacts, groups, campaigns, senders, and credits from Node.js, Deno, or the browser.
Maintainers
Readme
smsprosdk
Typed JavaScript/TypeScript SDK for SMS Pro platforms. Zero dependencies — works with Node.js 18+, Deno, and browsers.
Features
- Zero dependencies — uses the native
fetchAPI, no external libraries - Fully typed — complete TypeScript definitions for all methods, params, and responses
- Multi-platform — Node.js 18+, Deno, Bun, and modern browsers
- Simple auth — API key injected automatically into every request
- White-label ready — built specifically for white-label SMS platforms
Installation
# npm
npm install smsprosdk
# pnpm
pnpm add smsprosdk
# yarn
yarn add smsprosdk
# bun
bun add smsprosdkQuick Start
import { SmsProSDK } from "smsprosdk";
const sdk = new SmsProSDK({
baseUrl: "https://sms.myplatform.com", // or https://sms.myplatform.com/api
apiKey: "your-api-key",
});
// Send an SMS campaign
await sdk.campaigns.create({
text: "Hello @firstName, your order is ready!",
sender: "MyShop",
contacts: ["2250701020304", "2250705060708"],
});
// List senders
const { items: senders } = await sdk.senders.list();
console.log(senders);Table of Contents
- Configuration
- Contacts
- Groups
- Campaigns
- Sender IDs
- Pagination
- Error Handling
- TypeScript Types
- Platform Support
- License
Configuration
Create an SDK instance by providing your platform URL and API key.
import { SmsProSDK } from "smsprosdk";
const sdk = new SmsProSDK({
baseUrl: "https://sms.myplatform.com",
apiKey: "your-api-key",
});Options
| Option | Type | Required | Description |
| --------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
| baseUrl | string | Yes | Base URL of your SMS platform (e.g. "https://sms.myplatform.com"). The SDK appends /api automatically. |
| apiKey | string | Yes | Your API key. Found in Settings > Developer Access on the platform dashboard. |
| fetchFn | typeof fetch | No | Custom fetch implementation. Useful for environments without a global fetch or for testing with mocks. Defaults to the global fetch. |
Custom Fetch Example
import nodeFetch from "node-fetch";
const sdk = new SmsProSDK({
baseUrl: "https://sms.myplatform.com",
apiKey: "your-api-key",
fetchFn: nodeFetch as unknown as typeof fetch,
});Contacts
Manage your contact list — create, read, update, and delete contacts. Contacts store phone numbers and personal info used for SMS personalization with variables like @firstName.
List Contacts
Retrieve a paginated list of all contacts.
const result = await sdk.contacts.list();
console.log(result.items); // Contact[]
console.log(result.total); // Total number of contacts
console.log(result.page); // Current page numberWith pagination options:
const result = await sdk.contacts.list({
page: 2,
count: 50,
sort: "asc",
});Create a Contact
const contact = await sdk.contacts.create({
name: "Kouassi Aya",
phone: "2250701020304",
});
console.log(contact.id); // "abc123..."With all optional fields:
const contact = await sdk.contacts.create({
name: "Kouassi Aya",
phone: "2250701020304",
firstName: "Aya",
lastName: "Kouassi",
sex: "F",
});CreateContactParams
| Field | Type | Required | Description |
| ----------- | -------------- | -------- | ---------------------------------------------------------------- |
| name | string | Yes | Display name for the contact. |
| phone | string | Yes | Ivorian phone number with country code (e.g. "2250701020304"). |
| firstName | string | No | First name. Used for the @firstName SMS variable. |
| lastName | string | No | Last name. Used for the @lastName SMS variable. |
| sex | "M" | "F" | No | Gender of the contact. |
Get a Contact
const contact = await sdk.contacts.get("contact-id");
console.log(contact.name); // "Kouassi Aya"
console.log(contact.phone); // "2250701020304"
console.log(contact.firstName); // "Aya"Update a Contact
Update one or more fields on an existing contact. Only the fields you provide will be changed.
const updated = await sdk.contacts.update("contact-id", {
name: "Kouassi Aya Marie",
firstName: "Aya Marie",
});UpdateContactParams
| Field | Type | Description |
| ----------- | -------------- | ----------------- |
| name | string | New display name. |
| phone | string | New phone number. |
| firstName | string | New first name. |
| lastName | string | New last name. |
| sex | "M" | "F" | New gender. |
Delete a Contact
await sdk.contacts.delete("contact-id");Contact Object
| Field | Type | Description |
| ----------- | -------------- | --------------------------------------- |
| id | string | Unique identifier. |
| name | string | Display name. |
| phone | string | Phone number with country code 225. |
| firstName | string? | First name (for @firstName variable). |
| lastName | string? | Last name (for @lastName variable). |
| sex | "M" | "F" | Gender. |
| createdAt | string? | ISO 8601 creation date. |
Groups
Organize contacts into groups for targeted campaigns. Deleting a group does not delete the contacts inside it.
List Groups
const result = await sdk.groups.list();
for (const group of result.items) {
console.log(`${group.name}: ${group.count} members`);
}With pagination:
const result = await sdk.groups.list({ page: 1, count: 10, sort: "desc" });Create a Group
Create an empty group:
const group = await sdk.groups.create({
name: "VIP Clients",
});Create a group with existing contacts:
const group = await sdk.groups.create({
name: "VIP Clients",
contactIds: ["contact-id-1", "contact-id-2", "contact-id-3"],
});Create a group and create new contacts at the same time:
const group = await sdk.groups.create({
name: "New Prospects",
contacts: [
{ name: "Alice", phone: "2250701020304", firstName: "Alice" },
{ name: "Bob", phone: "2250705060708", firstName: "Bob" },
],
});Note: If a contact with the same phone number already exists, it will be reused automatically instead of creating a duplicate. This means you can safely pass contacts without worrying about duplicates.
CreateGroupParams
| Field | Type | Required | Description |
| ------------ | ----------------------- | -------- | --------------------------------------------------------------------------------------------------- |
| name | string | Yes | Group name (1-100 characters). |
| contactIds | string[] | No | IDs of existing contacts to add to the group. |
| contacts | CreateContactParams[] | No | New contacts to create and add to the group. Existing contacts (by phone) are reused automatically. |
Get a Group
const group = await sdk.groups.get("group-id");
console.log(group.name); // "VIP Clients"
console.log(group.count); // 42Update a Group
const updated = await sdk.groups.update("group-id", {
name: "Premium Clients",
});Replace the contacts in a group:
await sdk.groups.update("group-id", {
contactIds: ["contact-id-1", "contact-id-2"],
});UpdateGroupParams
| Field | Type | Description |
| ------------ | ---------- | ------------------------------------------------------------- |
| name | string | New group name. |
| contactIds | string[] | Contact IDs to set on the group (replaces existing contacts). |
Delete a Group
Deletes the group but keeps all contacts intact.
await sdk.groups.delete("group-id");Group Object
| Field | Type | Description |
| ----------- | --------- | ------------------------------- |
| id | string | Unique identifier. |
| name | string | Group name. |
| count | number? | Number of members in the group. |
| createdAt | string? | ISO 8601 creation date. |
Campaigns
Create and send SMS campaigns. A campaign sends a text message to a list of contacts or groups. Each SMS sent debits credits from your account balance.
SMS Variables
Personalize your SMS messages using @variableName. Built-in variables from contact data:
| Variable | Description | Example |
| ------------ | ---------------------- | -------------------------------------------------------------- |
| @name | Contact's display name | "Hello @name!" becomes "Hello Aya Kouassi!" |
| @firstName | Contact's first name | "Hello @firstName!" becomes "Hello Aya!" |
| @lastName | Contact's last name | "Dear @lastName," becomes "Dear Kouassi," |
| @phone | Contact's phone number | "Your number: @phone" becomes "Your number: 2250701020304" |
Custom Variables (XLSX / Contact Objects)
You can pass any custom field on a contact object, and it becomes a variable in the SMS text. This is useful when importing data from an XLSX file or any external source:
await sdk.campaigns.create({
text: "Hi @name, your order @order_id for @amount FCFA is ready!",
sender: "MyShop",
contacts: [
{
phone: "2250701020304",
name: "Aya",
order_id: "CMD-123",
amount: "5000",
},
{
phone: "2250705060708",
name: "Kofi",
order_id: "CMD-456",
amount: "3500",
},
],
});
// Aya receives: "Hi Aya, your order CMD-123 for 5000 FCFA is ready!"
// Kofi receives: "Hi Kofi, your order CMD-456 for 3500 FCFA is ready!"You can mix phone strings and contact objects in the same contacts array:
await sdk.campaigns.create({
text: "Hello @name!",
sender: "MyShop",
contacts: [
"2250709080706", // phone string — only built-in variables will work
{ phone: "2250701020304", name: "Aya", custom_field: "value" },
],
});Note: Variable names are normalized — use lowercase alphanumeric characters and underscores (e.g.
order_id,customer_name). Any field key on the contact object becomes an@variablein your SMS text.
Campaign Types
| Type | Description |
| ------------- | --------------------------------------------------------------- |
| "SMS" | Default. Sends immediately to all recipients. |
| "SCHEDULED" | Sends at a specific future date/time. Requires scheduledDate. |
| "RECURRING" | Sends repeatedly on specified days/times. Requires recurring. |
Send a Campaign Immediately
const campaign = await sdk.campaigns.create({
text: "Hello @firstName, your order #1234 is ready for pickup!",
sender: "MyShop",
contacts: ["2250701020304", "2250705060708"],
});
console.log(campaign.id); // Campaign ID
console.log(campaign.type); // "SMS"
console.log(campaign.contactCount); // Number of contacts targeted
console.log(campaign.creditUsed); // Credits consumedSend a Campaign to Groups
Target one or more groups instead of individual numbers:
const campaign = await sdk.campaigns.create({
text: "Flash sale! 50% off all items today only. Visit us now!",
sender: "MyShop",
groupsIds: ["group-id-1", "group-id-2"],
});Send a Campaign with a Name
Give your campaign a name to easily find it later in your history:
const campaign = await sdk.campaigns.create({
name: "March Promo 2026",
text: "Hi @firstName, enjoy 20% off this week with code MARCH20!",
sender: "MyShop",
contacts: ["2250701020304"],
});Schedule a Campaign for Later
Send an SMS at a future date and time. You must set type to "SCHEDULED":
const campaign = await sdk.campaigns.create({
type: "SCHEDULED",
text: "Happy New Year @firstName! Best wishes from our team.",
sender: "MyShop",
contacts: ["2250701020304"],
scheduledDate: "2026-12-31T23:59:00.000Z",
});
console.log(campaign.type); // "SCHEDULED"Note: The
scheduledDatemust be in the future. Credits are not debited until the campaign actually sends.
Set Up a Recurring Campaign
Send messages on specific days of the week at specified times. Set type to "RECURRING" and provide a recurring object with day names as keys and "HH:mm" time strings as values:
const campaign = await sdk.campaigns.create({
type: "RECURRING",
text: "Weekly reminder: check out our new arrivals!",
sender: "MyShop",
groupsIds: ["group-id"],
recurring: {
monday: "08:00",
wednesday: "14:30",
friday: "09:00",
},
});Set a day to null to skip it, or simply omit it:
const campaign = await sdk.campaigns.create({
type: "RECURRING",
text: "Good morning @firstName! Here are today's specials.",
sender: "MyShop",
contacts: ["2250701020304"],
recurring: {
monday: "07:00",
tuesday: "07:00",
wednesday: "07:00",
thursday: "07:00",
friday: "07:00",
saturday: null, // No send on Saturday
sunday: null, // No send on Sunday
},
});RecurringSchedule
| Field | Type | Description |
| ----------- | ---------------- | -------------------------------------------------------------- |
| monday | string \| null | Time to send on Monday in "HH:mm" format, or null to skip. |
| tuesday | string \| null | Time to send on Tuesday. |
| wednesday | string \| null | Time to send on Wednesday. |
| thursday | string \| null | Time to send on Thursday. |
| friday | string \| null | Time to send on Friday. |
| saturday | string \| null | Time to send on Saturday. |
| sunday | string \| null | Time to send on Sunday. |
Note: At least one day must have a time value. Credits are not debited until each recurring send actually fires.
Combine Contacts and Groups
You can target both individual phone numbers and entire groups in the same campaign:
const campaign = await sdk.campaigns.create({
text: "Important update for all our customers!",
sender: "MyShop",
contacts: ["2250709080706"],
groupsIds: ["group-id-1"],
});Note: You must provide at least one of
contactsorgroupsIds. Phone numbers are deduplicated across both.
CreateCampaignParams
| Field | Type | Required | Description |
| --------------- | ------------------- | -------- | --------------------------------------------------------------------------------------------------- |
| text | string | Yes | SMS text content. Supports variables: @firstName, @lastName, @phone. |
| sender | string | Yes | Accepted sender ID name (e.g. "MyShop"). Must be accepted before use. |
| name | string | No | Campaign name for identification in your history. |
| contacts | CampaignContact[] | No | Phone strings or contact objects with custom fields. Custom fields become @variables in SMS text. |
| groupsIds | string[] | No | Group IDs to target. All contacts in these groups will receive the SMS. |
| type | CampaignType | No | "SMS" (default), "SCHEDULED", or "RECURRING". |
| scheduledDate | string | No | ISO 8601 date for scheduled delivery. Required when type is "SCHEDULED". Must be in the future. |
| recurring | RecurringSchedule | No | Day/time schedule. Required when type is "RECURRING". At least one day must be set. |
List Campaigns
const result = await sdk.campaigns.list();
for (const campaign of result.items) {
console.log(
`${campaign.name} — ${campaign.type} — ${campaign.contactCount} contacts`,
);
}With pagination:
const result = await sdk.campaigns.list({ page: 1, count: 10 });Get a Campaign
const campaign = await sdk.campaigns.get("campaign-id");
console.log(campaign.text); // SMS content
console.log(campaign.type); // "SMS" | "SCHEDULED" | "RECURRING"
console.log(campaign.creditUsed); // Credits consumed
console.log(campaign.isEnabled); // true/false for scheduled/recurringUpdate a Campaign
Update a scheduled or recurring campaign (e.g. change the date or disable it). Cannot update "SMS" type campaigns.
// Reschedule a campaign
await sdk.campaigns.update("campaign-id", {
scheduledDate: "2026-04-01T10:00:00.000Z",
});
// Change recurring days
await sdk.campaigns.update("campaign-id", {
recurring: {
monday: "10:00",
friday: "10:00",
},
});
// Disable a campaign
await sdk.campaigns.update("campaign-id", {
isEnabled: false,
});UpdateCampaignParams
| Field | Type | Description |
| --------------- | ------------------- | ---------------------------------------------------------------------------------- |
| scheduledDate | string | New scheduled date (ISO 8601, must be in the future). For "SCHEDULED" campaigns. |
| recurring | RecurringSchedule | Updated day/time schedule. For "RECURRING" campaigns. |
| isEnabled | boolean | Enable or disable the campaign. |
Campaign Object
| Field | Type | Description |
| -------------- | -------------- | ---------------------------------------------------------- |
| id | string | Unique identifier. |
| name | string? | Campaign name. |
| text | string | SMS text content. |
| type | CampaignType | Campaign type: "SMS", "SCHEDULED", or "RECURRING". |
| isEnabled | boolean? | Whether the campaign is enabled (for SCHEDULED/RECURRING). |
| contactCount | number? | Number of contacts targeted. |
| creditUsed | number? | Credits consumed by this campaign. |
| createdAt | string? | ISO 8601 creation date. |
Sender IDs
A Sender ID is the alphanumeric name displayed as the sender on received SMS messages (e.g. "MyShop" instead of a phone number). New sender IDs require admin approval before they can be used in campaigns.
Approval Flow
- Create a sender ID with your business details — status is
"PENDING" - Wait for approval (up to 72 hours)
- Use the accepted sender in campaigns — status is
"ACCEPTED" - If refused, you can modify and resubmit — status resets to
"PENDING"
Modification Rules
| Status | Can Update | Can Delete | Can Use in Campaign |
| ------------ | ----------------------- | ---------- | ------------------- |
| "PENDING" | Yes | Yes | No |
| "ACCEPTED" | No | No | Yes |
| "REFUSED" | Yes (resets to PENDING) | No | No |
List All Senders
const { items } = await sdk.senders.list();
for (const sender of items) {
console.log(`${sender.name} — ${sender.status}`);
// "MyShop — ACCEPTED"
// "NewBrand — PENDING"
}Create a Sender ID
All fields are required when creating a sender:
const sender = await sdk.senders.create({
name: "MyShop",
description: "Used for order notifications and promotions",
email: "[email protected]",
website: "https://myshop.ci",
address: "Abidjan, Cocody, Rue des Jardins",
messageExample: "Bonjour @firstName, votre commande #123 est prete.",
types: ["TRANSACTIONAL", "PROMOTIONAL"],
});
console.log(sender.id); // "sender-abc123"
console.log(sender.status); // "PENDING"CreateSenderParams
| Field | Type | Required | Description |
| ---------------- | -------------- | -------- | ----------------------------------------------------- |
| name | string | Yes | Sender name. 3-11 alphanumeric characters, no spaces. |
| description | string | Yes | Describe how this sender ID will be used. |
| email | string | Yes | Contact email for your business. |
| website | string | Yes | Your company website URL. |
| address | string | Yes | Physical business address. |
| messageExample | string | Yes | An example of the SMS content you will send. |
| types | SenderType[] | Yes | Usage types. At least one required. |
Sender Types
| Value | Description |
| ----------------- | --------------------------------------------- |
| "PROMOTIONAL" | Marketing and promotional messages. |
| "OTP" | One-time password / verification codes. |
| "TRANSACTIONAL" | Order confirmations, receipts, notifications. |
Get a Sender
const sender = await sdk.senders.get("sender-id");
console.log(sender.name); // "MyShop"
console.log(sender.status); // "ACCEPTED"Update a Sender
Senders with "PENDING" or "REFUSED" status can be updated. Updating a "REFUSED" sender resets its status to "PENDING" so it re-enters the approval flow.
await sdk.senders.update("sender-id", {
name: "MyStore",
description: "Updated description for our store notifications",
types: ["TRANSACTIONAL"],
});Archive or unarchive a sender:
await sdk.senders.update("sender-id", {
archived: true,
});UpdateSenderParams
| Field | Type | Description |
| ---------------- | -------------- | ----------------------------------------------- |
| name | string | New sender name (3-11 alphanumeric, no spaces). |
| description | string | New usage description. |
| email | string | New contact email. |
| website | string | New website URL. |
| address | string | New physical address. |
| messageExample | string | New example SMS. |
| types | SenderType[] | New usage types. |
| archived | boolean | Archive or unarchive the sender. |
Delete a Sender
Only senders with "PENDING" status can be deleted:
await sdk.senders.delete("sender-id");Sender Object
| Field | Type | Description |
| ---------------- | ------------------------------------------ | ---------------------------------------------------------- |
| id | string | Unique identifier. |
| name | string | Sender name (3-11 alphanumeric). |
| status | "PENDING" | "ACCEPTED" | "REFUSED" | Approval status. |
| description | string? | Usage description. |
| email | string? | Contact email. |
| website | string? | Company website. |
| address | string? | Physical address. |
| messageExample | string? | Example SMS content. |
| types | SenderType[]? | Usage types ("PROMOTIONAL", "OTP", "TRANSACTIONAL"). |
| archived | boolean? | Whether the sender is archived. |
| createdAt | string? | ISO 8601 creation date. |
Pagination
All list endpoints that return multiple items support pagination via PaginationParams.
Parameters
| Field | Type | Default | Description |
| ------- | ------------------- | -------- | --------------------------------------- |
| page | number | 1 | Page number (1-based). |
| count | number | 50 | Number of items per page. Minimum: 5. |
| sort | "asc" | "desc" | "desc" | Sort order by creation date. |
Response
Every paginated response has this shape:
interface PaginatedResponse<T> {
items: T[]; // Items on the current page
total: number; // Total items across all pages
page: number; // Current page number
}Example: Paginating Through All Contacts
async function getAllContacts(sdk) {
const allContacts = [];
let page = 1;
let hasMore = true;
while (hasMore) {
const result = await sdk.contacts.list({ page, count: 100 });
allContacts.push(...result.items);
hasMore = allContacts.length < result.total;
page++;
}
return allContacts;
}Paginated Endpoints
| Endpoint | Resource |
| ---------------------- | --------- |
| sdk.contacts.list() | Contacts |
| sdk.groups.list() | Groups |
| sdk.campaigns.list() | Campaigns |
Note:
sdk.senders.list()returns all senders without pagination.
Error Handling
All SDK methods throw a SmsProError when an API request fails. This includes validation errors, authentication errors, and network errors.
SmsProError
import { SmsProSDK, SmsProError } from "smsprosdk";
try {
await sdk.contacts.create({ name: "Alice", phone: "invalid" });
} catch (err) {
if (err instanceof SmsProError) {
console.error(err.message); // Human-readable error message
console.error(err.statusCode); // HTTP status code (or 0 for network errors)
console.error(err.rawResponse); // Full JSON response from the server
}
}Error Properties
| Property | Type | Description |
| ------------- | --------- | ------------------------------------------------------------------------------- |
| message | string | Error message from the API (e.g. "Invalid phone number"). |
| statusCode | number | HTTP status code. 0 means a network error occurred (no response from server). |
| rawResponse | unknown | The full JSON response body from the server, useful for debugging. |
Common Error Scenarios
Authentication Error
try {
const sdk = new SmsProSDK({
baseUrl: "https://sms.myplatform.com",
apiKey: "wrong-key",
});
await sdk.contacts.list();
} catch (err) {
if (err instanceof SmsProError) {
console.error(err.statusCode); // 401
console.error(err.message); // "Invalid API key"
}
}Validation Error
try {
await sdk.contacts.create({ name: "", phone: "" });
} catch (err) {
if (err instanceof SmsProError) {
console.error(err.statusCode); // 400
console.error(err.message); // Validation error message
}
}Network Error
try {
const sdk = new SmsProSDK({
baseUrl: "https://unreachable.example.com",
apiKey: "key",
});
await sdk.contacts.list();
} catch (err) {
if (err instanceof SmsProError) {
console.error(err.statusCode); // 0
console.error(err.message); // "fetch failed" or similar
}
}Sender Not Accepted
try {
await sdk.campaigns.create({
text: "Hello!",
sender: "PendingSender",
contacts: ["2250701020304"],
});
} catch (err) {
if (err instanceof SmsProError) {
console.error(err.message); // Sender not accepted
}
}Insufficient Credits
try {
await sdk.campaigns.create({
text: "Bulk message",
sender: "MyShop",
groupsIds: ["large-group-id"],
});
} catch (err) {
if (err instanceof SmsProError) {
console.error(err.message); // Insufficient credits
}
}TypeScript Types
All types are exported from the main package entry point:
import type {
// Configuration
SmsProSDKConfig,
// Common
PaginationParams,
PaginatedResponse,
// Contacts
Contact,
CreateContactParams,
UpdateContactParams,
// Groups
Group,
CreateGroupParams,
UpdateGroupParams,
// Campaigns
CampaignContact,
CampaignType,
RecurringSchedule,
Campaign,
CreateCampaignParams,
UpdateCampaignParams,
// Senders
SenderType,
Sender,
CreateSenderParams,
UpdateSenderParams,
} from "smsprosdk";Resource Reference
Quick reference of all available resources and methods:
| Resource | Method | Description |
| --------------- | -------------------- | ------------------------------------- |
| sdk.contacts | list(params?) | List contacts with pagination |
| | create(params) | Create a new contact |
| | get(id) | Get contact by ID |
| | update(id, params) | Update a contact |
| | delete(id) | Delete a contact |
| sdk.groups | list(params?) | List groups with pagination |
| | create(params) | Create a group |
| | get(id) | Get group by ID |
| | update(id, params) | Update a group |
| | delete(id) | Delete a group |
| sdk.campaigns | list(params?) | List campaigns with pagination |
| | create(params) | Create and send a campaign |
| | get(id) | Get campaign by ID |
| | update(id, params) | Update a scheduled/recurring campaign |
| sdk.senders | list() | List all sender IDs |
| | create(params) | Create a sender ID |
| | get(id) | Get sender by ID |
| | update(id, params) | Update a PENDING or REFUSED sender |
| | delete(id) | Delete a PENDING sender |
Platform Support
The SDK uses the native fetch API and works in any environment that supports it:
| Platform | Minimum Version | | -------- | ------------------- | | Node.js | 18+ | | Deno | All versions | | Bun | All versions | | Browsers | All modern browsers |
For older environments that don't have a global fetch, pass a custom implementation:
import fetch from "node-fetch";
const sdk = new SmsProSDK({
baseUrl: "https://sms.myplatform.com",
apiKey: "your-api-key",
fetchFn: fetch as unknown as typeof fetch,
});License
MIT
