npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@foxscheduling/sdk

v0.2.7

Published

Official Fox Scheduling Partner API SDK

Readme

@foxscheduling/sdk

Official Node.js SDK for the Fox Scheduling Partner API.

Use this package to read and manage bookings, customers, availability, and more on behalf of a Fox Scheduling business — from your own server or integration.

Requirements: Node.js 18+

Full API reference: foxscheduling.com/developers


Install

npm install @foxscheduling/sdk @foxscheduling/shared

Both packages are required. @foxscheduling/shared provides shared types and scope names.


Choose how to authenticate

| Method | Best for | Dashboard setup | | --- | --- | --- | | API key | Scripts, cron jobs, or automation for your own Fox business | Developers → API keys | | OAuth | SaaS apps where other people connect their Fox business to your product | Developers → OAuth apps |

If you only need to access a business you own, start with an API key. It is the fastest path.


Quick start: API key

1. Create a key

  1. Sign in to Fox Scheduling.
  2. Open Developers → API keys.
  3. Pick the business, choose scopes (e.g. business:read, bookings:read), and create a key.
  4. Copy the key immediately — it is shown only once. Keys look like fx_live_....

2. Call the API

import { FoxScheduling } from "@foxscheduling/sdk";

const fox = new FoxScheduling({
  auth: {
    type: "apiKey",
    apiKey: process.env.FOX_API_KEY!, // fx_live_...
  },
});

// Example: read the connected business profile
const business = await fox.business.get();
console.log(business.name);

Available resources

After creating a client, use:

  • fox.business — business profile
  • fox.services — services
  • fox.staff — staff members
  • fox.availability — availability rules
  • fox.bookings — bookings
  • fox.customers — customers
  • fox.webhooks — webhook subscriptions

All methods are fully typed (no unknown inputs or outputs) and throw a FoxSchedulingError on API errors.

Lists are paginated (max 100 per page)

Pagination is enforced by the API — every list() method sends limit/offset to the server and returns a typed Page<T>, never a raw array:

interface Page<T> {
  items: T[]; // at most 100
  total: number; // total across all pages
  limit: number; // effective page size
  offset: number; // current offset
  hasMore: boolean; // true if more items exist
}

Pass limit (1–100, default 100) and offset to page through results:

const first = await fox.customers.list({ limit: 50, offset: 0 });
console.log(first.items.length, first.total, first.hasMore);

if (first.hasMore) {
  const next = await fox.customers.list({ limit: 50, offset: 50 });
}

limit is clamped to a maximum of 100.

Bookings require a month

fox.bookings.list() requires a month (format YYYY-MM) so responses stay small. Other filters are optional and typed:

const page = await fox.bookings.list({
  month: "2026-06", // required
  status: "booked", // optional, typed union
  serviceId: "…", // optional
  limit: 100, // optional
});

Omitting or malformatting month throws a FoxSchedulingError before any request is sent.

Typed create & update

Create and update bodies are typed — for example:

// Create a customer
const customer = await fox.customers.create({
  name: "Jane Doe",
  email: "[email protected]",
});

// Create a booking
const result = await fox.bookings.create({
  serviceId: "…",
  name: "Jane Doe",
  email: "[email protected]",
  slots: [{ date: "2026-06-12", start: "09:00", end: "09:30" }],
});
console.log(result.bookingSummary, result.customerId);

Get booking embed code

fox.business.getEmbedCode() returns ready-to-paste embed snippets for the connected business: an inline iframe, a popup <script> widget, and the raw bookingUrl. All options are optional and typed:

const embed = await fox.business.getEmbedCode({
  theme: "dark", // "light" | "dark" | "auto" (default)
  servicePath: "haircut", // optional: link straight to one service
  hidePageDetails: false, // hide business header inside the embed
  heightPx: 760, // inline iframe height
  iframeBorderRadius: "medium", // none | sm | medium | lg | xl | 2xl
  openMode: "popup", // "popup" | "newTab" for the script widget
  buttonText: "Book now", // popup button label
});

console.log(embed.bookingUrl); // shareable URL
console.log(embed.iframe); // <style>…</style><div>…<iframe …></div>
console.log(embed.popupScript); // <script … data-booking-url="…"></script>
console.log(embed.appliedOptions); // options after defaults applied

By default the popup widget renders its own button. To open the popup from a button that already exists on your page, set buttonMode: "existing" and pass the element's id (without #) as triggerId:

// For an element like <button id="book-appointment">Book</button>
const embed = await fox.business.getEmbedCode({
  buttonMode: "existing",
  triggerId: "book-appointment",
});

// embed.popupScript now wires the popup to your element instead of injecting a button.
// embed.appliedOptions.buttonMode === "existing"
// embed.appliedOptions.triggerId === "book-appointment"

An invalid or missing triggerId while buttonMode is "existing" is rejected by the API.


OAuth: connect other users' businesses

Use OAuth when you are building an integration (e.g. a CRM or reporting tool) and Fox Scheduling users need to authorize your app to access their business.

OAuth is a browser-based flow. At a high level:

Your app                    Fox Scheduling              User's browser
   |                              |                            |
   |-- 1. Build authorize URL --->|                            |
   |-- 2. Redirect user -------------------------------------->|
   |                              |<-- 3. User signs in & allows
   |<-- 4. Redirect to your URL with ?code=... ---------------|
   |-- 5. Exchange code for tokens ->|                          |
   |-- 6. Call Partner API with stored tokens                   |

You never type the authorization code yourself. Fox appends it to your redirect URL after the user clicks Allow.

Before you write code

  1. Create an OAuth app under Developers → OAuth apps.
  2. Note the client ID and client secret (secret is shown once).
  3. Add a redirect URI — the exact URL Fox will send users back to, e.g. https://your-app.com/oauth/callback. It must match character-for-character in your code.

Step 1 — Send the user to Fox

Create the SDK client and build the authorization URL:

import { FoxScheduling, FileTokenStore } from "@foxscheduling/sdk";

const fox = new FoxScheduling({
  auth: {
    type: "oauth",
    clientId: process.env.FOX_CLIENT_ID!,
    clientSecret: process.env.FOX_CLIENT_SECRET!,
    redirectUri: "https://your-app.com/oauth/callback", // must match dashboard
    tokenStore: new FileTokenStore("./fox-tokens.json"),
  },
});

const { url, codeVerifier, state } = fox.oauth!.getAuthorizationUrl({
  scopes: ["business:read", "bookings:read"],
});

Important: Before redirecting the user, save codeVerifier and state somewhere tied to that user session (memory, Redis, database, encrypted cookie, etc.). You need them on the next step.

Then redirect the user's browser to url (e.g. res.redirect(url) in Express).

Step 2 — Handle the callback

When the user approves, Fox redirects to your redirectUri with query parameters:

https://your-app.com/oauth/callback?code=AUTHORIZATION_CODE&state=SAME_STATE_YOU_SAVED

In your callback route:

  1. Read code and state from the query string.
  2. Confirm state matches what you saved (prevents CSRF).
  3. Load the saved codeVerifier for that session.
  4. Exchange the code for tokens.
// Example: Express route GET /oauth/callback
app.get("/oauth/callback", async (req, res) => {
  const code = req.query.code as string | undefined;
  const returnedState = req.query.state as string | undefined;

  if (!code) {
    return res.status(400).send("Authorization was denied or failed.");
  }
  if (returnedState !== savedState) {
    return res.status(400).send("Invalid state — possible CSRF.");
  }

  await fox.oauth!.exchangeCode(code, savedCodeVerifier);

  // Tokens are saved automatically. Redirect to your app.
  res.redirect("/dashboard");
});

Step 3 — Use the API

Once exchangeCode succeeds, the SDK stores tokens and attaches them to every request. You can call the same resources as with an API key:

const business = await fox.business.get();
const bookings = await fox.bookings.list({ month: "2026-06" });

The SDK refreshes access tokens automatically before they expire.

What is fox-tokens.json?

In the examples above, FileTokenStore("./fox-tokens.json") tells the SDK where to save OAuth tokens on disk.

  • You do not create this file. The SDK writes it after a successful exchangeCode.
  • The path is just an example — use any path you like.
  • Keep it secret — it contains refresh tokens. Add it to .gitignore.
  • One file = one connection. Fine for local scripts or a single test business.

Example contents (written by the SDK):

{
  "accessToken": "eyJhbGciOiJSUzI1NiIs...",
  "refreshToken": "opaque-refresh-token",
  "expiresAt": 1710003600000,
  "scope": "business:read bookings:read",
  "tokenType": "Bearer"
}

Production OAuth apps

If many Fox users will connect their businesses, do not use one shared file. Implement the TokenStore interface and save tokens in your database, keyed by your user ID (or Fox tenant_id):

import type { TokenStore, TokenSet } from "@foxscheduling/sdk";

class DatabaseTokenStore implements TokenStore {
  constructor(private userId: string) {}
  async load(): Promise<TokenSet | null> { /* read from DB */ }
  async save(tokens: TokenSet): Promise<void> { /* write to DB */ }
  async clear(): Promise<void> { /* delete from DB */ }
}

For unit tests, use MemoryTokenStore() (tokens disappear when the process exits).

More detail: OAuth2 integration guide


Webhook signature verification

When Fox sends a webhook to your server, verify the X-Fox-Signature header:

import { verifyFoxWebhookSignature } from "@foxscheduling/sdk";

const ok = verifyFoxWebhookSignature({
  secret: process.env.FOX_WEBHOOK_SECRET!,
  timestamp: req.headers["x-fox-timestamp"],
  rawBody: req.rawBody, // unparsed request body string
  signatureHex: req.headers["x-fox-signature"],
});

if (!ok) {
  return res.status(401).send("Invalid signature");
}

Your HTTP framework must give you the raw body (before JSON parsing) for verification to work.


Scopes

Each API key or OAuth request uses scopes such as:

business:read, services:read, staff:read, availability:read, availability:write, bookings:read, bookings:write, customers:read, customers:write, webhooks:read, webhooks:write

Request only the scopes your integration needs. See the scope reference.


Links

Support

Questions or integration help: foxscheduling.com/contact