@izak0s/spacebring-api - v7.1.0
    Preparing search index...

    @izak0s/spacebring-api - v7.1.0

    spacebring-api

    CI npm docs license

    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.

    • 188 operations across 21 resource groups β€” the full Spacebring API surface
    • Auto-generated from the official OpenAPI spec β€” types and facade regenerate anytime the spec changes
    • Nested, discoverable API β€” sb.billing.invoices.pay(id), sb.visitors.visits.checkIn(body)
    • Auto-pagination β€” every paginated list endpoint has an iterate() async generator that walks nextPageToken for you
    • Ergonomic payloads β€” single-property envelopes are unwrapped in both directions: responses hand you the entity or array directly, and create/update methods take the payload flat (sb.plans.create({ title, price }) instead of { plan: { … } })
    • Readable, named types β€” entities (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 unions
    • Rich error handling β€” non-2xx responses throw a typed SpacebringError carrying the status, parsed body, and the operation that failed; malformed 2xx bodies and stuck pagination tokens throw instead of failing silently
    • Resilient by default β€” automatic retries for rate limits, gateway errors, and network failures (never replaying non-idempotent requests); optional per-attempt timeouts and AbortSignal cancellation on every method
    • Zero runtime dependencies β€” Node β‰₯ 20, fetch-based; type declarations are fully self-contained (TypeScript β‰₯ 5.4)
    • Dual module β€” ships both ESM and CommonJS builds with type declarations for each

    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:

    • Dates (createDate, startDate, …) are ISO 8601 strings β€” wrap in new Date(booking.startDate) when you need a Date.
    • Money (amount, price, …) arrives as decimal floats. Fine for display; for accounting arithmetic convert to integer cents first to avoid floating-point drift.
    • IDs (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.


    MIT