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

@proximityinsight/booking-sdk

v2.0.2

Published

TypeScript SDK for the Proximity Booking Widget — scoped to /v1/booking/*

Downloads

990

Readme

@proximityinsight/booking-sdk

A TypeScript SDK for embedding appointment booking into your website. Fully typed, scoped to the guest booking endpoints (/v1/booking/*).

Install

npm install @proximityinsight/booking-sdk

Requirements: Node.js 18+ (or any runtime with native fetch)

Prerequisites

Before integrating, ensure you have the following:

| Item | Description | |------|-------------| | Tenant ID | Your unique tenant identifier, provided during onboarding | | API Base URL | https://{environment}.proximityinsight.com — environment provided during onboarding | | Domain Registration | Your website domain must be registered for access — contact support |

Quick Start

import { createBookingClient } from '@proximityinsight/booking-sdk';

const client = createBookingClient({
  baseUrl: 'https://{environment}.proximityinsight.com',
});

// 1. Start a guest session
const { data: session } = await client.POST('/v1/booking/session', {
  body: { tenantId: 'YOUR_TENANT_ID' },
});

// 2. Attach the token to all subsequent requests
client.use({
  async onRequest({ request }) {
    request.headers.set('Authorization', `Bearer ${session.token}`);
    return request;
  },
});

// 3. Browse locations
const { data: locations } = await client.GET('/v1/booking/locations');

Authentication

All requests require a guest session token. No user credentials are needed.

Getting a Token

Create a session by calling the session endpoint with your tenant identifier:

const { data: session } = await client.POST('/v1/booking/session', {
  body: { tenantId: 'YOUR_TENANT_ID' },
});
// { token: "eyJhbG...", expiresInSeconds: 900 }

The token is valid for 15 minutes.

Domain Registration

Requests to the session endpoint are validated against your registered domains. Your domain must be registered before you can obtain a token. Contact support to add or update your allowed domains.

Attaching the Token

Use the .use() middleware to include the token on every request:

let currentToken = session.token;

client.use({
  async onRequest({ request }) {
    request.headers.set('Authorization', `Bearer ${currentToken}`);
    return request;
  },
});

Refreshing a Session

Tokens cannot be extended. When a token expires, create a new session:

const { data: newSession } = await client.POST('/v1/booking/session', {
  body: { tenantId: 'YOUR_TENANT_ID' },
});
currentToken = newSession.token;

We recommend creating a new session proactively before the 15-minute window elapses to avoid failed requests.

Booking Flow

A typical booking follows these steps in order.

1. Load Configuration

Retrieve widget settings that control the booking experience.

const { data: config } = await client.GET('/v1/booking/config');

Returns configuration for controlling widget behaviour, such as enabled features, display options, and booking rules.

2. Browse Locations

Fetch bookable locations, optionally filtered by proximity or product availability.

const { data: locations } = await client.GET('/v1/booking/locations', {
  params: {
    query: { lat: 51.5074, lng: -0.1278, productId: 'prod_001' },
  },
});

Pass lat and lng to sort results by distance. productId filters to locations that offer that product.

3. Browse Products

Fetch bookable products, optionally scoped to a specific location.

const { data: products } = await client.GET('/v1/booking/products', {
  params: {
    query: { locationId: 'loc_001' },
  },
});

4. Browse Staff

Fetch available staff at a location, optionally filtered by product.

const { data: tenantUsers } = await client.GET('/v1/booking/tenant-users', {
  params: {
    query: { locationId: 'loc_001', productId: 'prod_001' },
  },
});

locationId is required. productId is optional and narrows results to staff qualified for that product.

5. Check Available Dates

Retrieve dates that have availability within the booking window.

const { data: dates } = await client.GET('/v1/booking/availability/dates', {
  params: {
    query: {
      productId: 'prod_001',
      locationId: 'loc_001',
      tenantUserId: 'staff_001',
    },
  },
});

productId and locationId are required. tenantUserId is optional. The response includes the booking window boundaries.

6. Check Available Slots

Retrieve time slots for a specific date.

const { data: slots } = await client.GET('/v1/booking/availability/slots', {
  params: {
    query: {
      productId: 'prod_001',
      date: '2026-04-15',
      locationId: 'loc_001',
      tenantUserId: 'staff_001',
    },
  },
});

productId, locationId, and date are required. tenantUserId is optional.

7. Hold Appointment

Create a temporary hold on the selected slot. The hold expires after 10 minutes by default (tenant-configurable).

const { data: appointment } = await client.POST('/v1/booking/appointments', {
  body: {
    locationId: 'loc_001',
    productId: 'prod_001',
    tenantUserId: 'staff_001',
    date: '2026-04-15',
    startTime: '10:00:00',
  },
});

8. Create or Match Customer

Submit customer details. By default the system matches an existing record by email and creates a new one if no match is found. Phone-based matching is available as an opt-in tenant configuration.

const { data: customerRecord } = await client.POST('/v1/booking/customers', {
  body: {
    firstName: 'Jane',
    lastName: 'Doe',
    email: '[email protected]',
    phone: '+44 7700 900000',
  },
});
// Returns: { customerId: "..." }

9. Confirm Appointment

Attach the customer to the held appointment and confirm it. This will fail if the hold has expired.

const { data: confirmed } = await client.POST(
  '/v1/booking/appointments/{id}/confirm',
  {
    params: { path: { id: appointment.appointmentId } },
    body: { customerId: customerRecord.customerId },
  },
);

If the hold has expired, start again from step 7.

10. Release a Hold

Cancel a held appointment before confirmation to free the slot.

await client.DELETE('/v1/booking/appointments/{id}', {
  params: { path: { id: appointment.appointmentId } },
});
// 204 No Content

Managing Appointments

Get Appointment Detail

const { data } = await client.GET('/v1/booking/appointments/{id}', {
  params: { path: { id: 'apt_001' } },
});

Update Appointment

Send only the fields you want to change. The server validates availability before applying.

const { data, error } = await client.PATCH('/v1/booking/appointments/{id}', {
  params: { path: { id: 'apt_001' } },
  body: {
    date: '2026-04-16',
    startTime: '14:00:00',
    tenantUserId: 'staff_002',
  },
});

if (error) {
  // Handle validation or availability conflict
}

Cancel Appointment

Cancellation is permanent. Ensure confirmation is collected from the end user before calling this endpoint.

await client.POST('/v1/booking/appointments/{id}/cancel', {
  params: { path: { id: 'apt_001' } },
  body: { cancellationReason: 'Customer requested cancellation' },
});

Translations

Retrieve localised UI strings for multi-language support.

const { data: strings } = await client.GET('/v1/booking/translations');

Strings are managed per-tenant in the Proximity platform.

Using Types

All request and response types are exported from the SDK.

import type { Schemas } from '@proximityinsight/booking-sdk';

type BookingProduct = Schemas['BookingProductResponse'];
type BookingLocation = Schemas['BookingLocationResponse'];
type BookingTenantUser = Schemas['BookingTenantUserResponse'];
type BookingTimeSlot = Schemas['BookingTimeSlot'];
type AppointmentHold = Schemas['BookingAppointmentHoldResponse'];
type AppointmentConfirm = Schemas['BookingAppointmentConfirmResponse'];
type AppointmentDetail = Schemas['BookingAppointmentDetailResponse'];
type CancelResult = Schemas['BookingAppointmentCancelResponse'];
type AvailableDates = Schemas['BookingAvailableDatesResponse'];
type AvailableSlots = Schemas['BookingAvailableSlotsResponse'];
type BookingCustomer = Schemas['BookingCustomerResponse'];
type BookingConfig = Schemas['BookingConfigResponse'];

Error Handling

Every SDK method returns { data, error, response }. On a non-2xx response, data is undefined, error contains the parsed response body, and response is the standard Response object. Branch on response.status -- it is the authoritative, type-safe source of the HTTP status code. The error payload follows the API's error schema (typically { error: { statusCode, name, message } } for LoopBack endpoints).

const { data, error, response } = await client.POST('/v1/booking/appointments', {
  body: appointmentRequest,
});

if (error) {
  switch (response.status) {
    case 400:
      // Validation failure — missing or invalid fields
      showValidationError(error.error?.message);
      break;
    case 401:
      // Session expired — create a new session
      redirectToSessionStart();
      break;
    case 409:
      // Slot no longer available — prompt for a different time
      showSlotUnavailable();
      break;
    default:
      showGenericError();
  }
  return;
}

// data is fully typed
console.log(data.appointmentId);

Endpoints

| Method | Endpoint | Description | |--------|----------|-------------| | POST | /v1/booking/session | Start a new booking session | | GET | /v1/booking/config | Retrieve booking configuration | | GET | /v1/booking/locations | List available locations | | GET | /v1/booking/products | List bookable products | | GET | /v1/booking/tenant-users | List available staff members | | GET | /v1/booking/availability/dates | Get available dates | | GET | /v1/booking/availability/slots | Get available time slots | | POST | /v1/booking/customers | Create or match a customer | | POST | /v1/booking/appointments | Hold an appointment slot | | GET | /v1/booking/appointments/{id} | Get appointment details | | PATCH | /v1/booking/appointments/{id} | Update an appointment | | POST | /v1/booking/appointments/{id}/confirm | Confirm a held appointment | | POST | /v1/booking/appointments/{id}/cancel | Cancel an appointment | | POST | /v1/booking/appointments/auto-assign | Create a new held appointment with an auto-selected staff member (original hold unchanged) | | DELETE | /v1/booking/appointments/{id} | Release a held appointment | | GET | /v1/booking/translations | Retrieve UI translation strings |

Support

For help with integration, contact your Proximity account manager:

  • Tenant ID and API base URL
  • Domain registration
  • General integration questions