A fully-typed TypeScript client for the Spacebring coworking space management API, auto-generated from the official OpenAPI spec.
Community package β This is not an official Spacebring package. It is independently developed and maintained by the community. Use at your own risk. For the official API documentation, see spacebring.com/docs/api.
π Full API reference β every resource, method, and type, generated from the source.
sb.billing.invoices.pay(id), sb.visitors.visits.checkIn(body)iterate() async generator that walks nextPageToken for yousb.plans.create({ title, price }) instead of { plan: { β¦ } })Booking, Invoice) and query parameters (GetBookingsQuery) are exported named types, so hovers show Booking[] or { invoice?: Invoice; payment?: Payment } instead of generated type soup, and enum filters are literal unionsSpacebringError carrying the status, parsed body, and the operation that failed; malformed 2xx bodies and stuck pagination tokens throw instead of failing silentlyAbortSignal cancellation on every methodfetch-based; type declarations are fully self-contained (TypeScript β₯ 5.4)npm install @izak0s/spacebring-api
No runtime dependencies β HTTP uses the built-in fetch.
import { Spacebring, SpacebringError } from "@izak0s/spacebring-api";
const sb = new Spacebring({
clientId: process.env.SPACEBRING_CLIENT_ID!,
clientSecret: process.env.SPACEBRING_CLIENT_SECRET!,
networkId: process.env.SPACEBRING_NETWORK_ID, // optional β sent as the spacebring-network-id header
});
// Single-property envelopes are unwrapped β list() gives you Location[] directly.
const locations = await sb.locations.list();
const locationRef = locations[0].id;
// Paginated lists return the page envelope, so nextPageToken stays availableβ¦
const { benefits, nextPageToken } = await sb.benefits.list({ locationRef });
// β¦or hand it to iterate(), which follows nextPageToken across pages.
// Break out early and it simply stops fetching β no wasted requests.
for await (const booking of sb.resources.bookings.iterate({ locationRef })) {
console.log(`${booking.startDate} β ${booking.endDate}`);
}
// Non-2xx responses throw a typed SpacebringError.
try {
await sb.billing.invoices.get("does-not-exist");
} catch (error) {
if (error instanceof SpacebringError) {
console.error(`${error.status} on ${error.operation}: ${error.body?.message}`);
}
}
Writes take the payload directly β no { plan: { β¦ } } wrapper around request bodies:
const plan = await sb.plans.create({ locationRef, title: "Day pass bundle", price: 99, period: "month" });
await sb.plans.update(plan.id, { price: 149 });
Endpoints that genuinely send or return more than one payload keep the envelope intact:
const { invoice, payment } = await sb.billing.invoices.pay(invoiceId, {
paymentMethod: { type: "stripe" },
});
Entity types are exported by name β import type { Booking, Invoice, Membership } from "@izak0s/spacebring-api" β matching what the methods return (get/create/update resolve to the entity, iterate() yields it). Query parameters get named interfaces too (GetBookingsQuery, GetInvoicesQuery), with per-field docs from the spec and enum filters as literal unions, and request bodies get named types (CreateBookingBody, UpdateInvoiceBody). Lower-level helpers too: SpacebringConfig, SpacebringResources, and the raw spec types paths / components / operations.
Values are passed through exactly as the API sends them β no runtime conversion:
createDate, startDate, β¦) are ISO 8601 strings β wrap in new Date(booking.startDate) when you need a Date.amount, price, β¦) arrives as decimal floats. Fine for display; for accounting arithmetic convert to integer cents first to avoid floating-point drift.id, *Ref) are UUID strings.HTTP Basic with your Client ID and Client Secret from Spacebring β [Network] β Network Settings β Developers. The client builds the Authorization: Basic β¦ header for you. The API's OAuth2 flow is not currently supported.
Because those credentials ride on every request, the client rejects a non-https baseUrl at construction β http is allowed only for loopback hosts (local proxies or mock servers). The default baseUrl is https://api.spacebring.com.
For development without touching live data, Spacebring offers a test environment (Network settings β Billing add-on) with free sandbox API credentials that work with this client unchanged.
Non-2xx responses throw SpacebringError:
import { SpacebringError } from "@izak0s/spacebring-api";
try {
await sb.benefits.get(id);
} catch (error) {
if (error instanceof SpacebringError) {
console.error(error.status, error.body?.message);
console.error(error.operation); // "GET /benefits/v1/{benefitId}"
console.error(error.url); // the full request URL
}
}
Malformed successes are covered too: a 2xx with an empty or incomplete body throws a SpacebringError (never a bare TypeError), and iterate() throws instead of looping forever if the API repeats a page token.
The API allows 10 requests per second. Rate-limited requests (429) are retried automatically β up to 3 times, honoring Retry-After (seconds or HTTP-date) or backing off exponentially β so iterate() survives the limit out of the box. Gateway errors (502/503/504), network failures, and timeouts are retried the same way, but only for idempotent methods (GET/PUT/DELETE) β a POST is never replayed, since the request may have reached the API. Tune or disable via maxRetries in the config (maxRetries: 0 turns it off); an error that persists past the retries is thrown as-is.
Every method accepts a trailing options argument with an AbortSignal; aborting cancels the in-flight request and any pending retry wait. A client-wide per-attempt timeout is available via timeoutMs:
const sb = new Spacebring({ clientId, clientSecret, timeoutMs: 15_000 });
const controller = new AbortController();
const benefits = await sb.benefits.list({ locationRef }, { signal: controller.signal });
timeoutMs uses AbortSignal.timeout; combining it with your own signal relies on AbortSignal.any (Node β₯ 20.3, all modern browsers/workers/edge runtimes).
sb.raw is a typed openapi-fetch client for anything the facade doesn't expose (custom headers, response inspection):
const { data, error, response } = await sb.raw.GET("/networks/v1", {});
The whole client (types + methods) is generated from Spacebring's OpenAPI spec; a daily GitHub Action picks up spec changes and publishes a new version automatically. Details in CONTRIBUTING.md.