Skip to content
← BlogDeveloper

The @norbelys/sdk quickstart: from npm install to your first API call

Install the official Node/TypeScript SDK, authenticate with an API key, and make your first typed call against the Norbelys API — a complete quickstart.

By Gabriel Lara, Developer Relations, Norbelys

Founder-reviewed ·How we research and correct articles

If you’re building on Node or TypeScript and you’d rather not hand-write fetch calls against the Norbelys REST API, @norbelys/sdk is the official client. It’s generated straight from our OpenAPI spec — every resource, every field, every parameter is typed — so your editor tells you what’s available instead of a browser tab full of docs.

This is a start-to-finish quickstart: install the package, get an API key, make your first authenticated call, and know where to go next.

What you’re installing

@norbelys/sdk wraps the same REST API that powers the Norbelys dashboard — there’s no separate “SDK-only” surface. Under the hood it’s a typed HTTP client: it builds requests, attaches your Authorization header, parses the JSON response, and gives you back typed objects instead of any. If you already know the API shape from the OpenAPI spec, the SDK will feel immediately familiar — it’s the same resources, the same fields, just with autocomplete.

Before you start: get an API key

Every call to the Norbelys API needs an org-scoped API key. Create one from the dashboard, then keep it out of source control — treat it like any other production secret. If you’re building something that runs on a schedule or in CI rather than interactively, an API key is exactly the right credential (interactive AI agents have a separate OAuth 2.1 path, described in auth.md, which doesn’t apply here).

From zero to a first call

  1. Install the package

    Add @norbelys/sdk to your project with npm, pnpm, or bun. It ships its own TypeScript types, so there's no separate @types package to install.

  2. Set your API key

    Store it as an environment variable (NORBELYS_API_KEY is the conventional name) rather than a literal string in your code. The SDK reads it when you construct the client.

  3. Construct the client

    Create a client instance once, near your app's entry point, and reuse it — the same pattern you'd use for any HTTP client SDK (a Stripe or Twilio client, for example).

  4. Make your first call

    List a resource you already have data for — people or senders are good first calls — and log the result to confirm the round trip works end to end.

  5. Move to a real operation

    Once a read call succeeds, try a write: create a sender, or send a single message through the one send door. Typed inputs mean mistakes surface at compile time, not in production.

Install and run

Terminal
$ npm install @norbelys/sdkadded 1 package in 1s$ export NORBELYS_API_KEY="ak_..."$ node index.js{ id: "con_...", email: "jane@acme.com", givenName: "Jane" }

A minimal script looks like this. Construct the client with your key, then call a resource method — the shape mirrors the REST endpoint one-to-one (GET /v1/people becomes something like client.people.list()):

import { Norbelys } from "@norbelys/sdk";

const client = new Norbelys({ apiKey: process.env.NORBELYS_API_KEY });

const { data } = await client.people.list({ limit: 5 });

for (const person of data) {
  console.log(person.email, person.givenName);
}

Because the client is generated from the OpenAPI spec, person here isn’t any — your editor knows it has email, givenName, familyName, and the rest of the Schema.org-mapped fields the API returns, and it’ll flag a typo in a field name before you ever run the code.

A first write: creating a sender

Reads are a good sanity check, but the SDK earns its keep on writes, where typed inputs catch mistakes immediately. Creating a sender is a common first write call — every send in Norbelys goes out through one:

const sender = await client.senders.create({
  email: "outreach@yourdomain.com",
  name: "Outreach",
});

console.log(sender.id); // snd_...

If you leave off a required field, or pass a string where the type expects a number, that’s a compile error in your editor — not a 422 you discover after deploying. That’s the whole value proposition of a generated client over raw fetch: the contract is enforced before the request ever leaves your machine.

Handling errors

Every API error comes back in one shape — { error: { code, message, field? } } — and the SDK surfaces that as a typed exception you can catch and branch on, rather than a bag of possibly-undefined fields:

try {
  await client.senders.create({ email: "not-an-email" });
} catch (err) {
  if (err instanceof Norbelys.APIError) {
    console.error(err.code, err.message, err.field);
  }
}

Pagination

List endpoints use cursor pagination — never offset — because IDs are prefixed cuid2 and aren’t time-sortable, so a stable cursor over (createdAt, id) is the only correct way to page through a growing list. Expect the SDK to expose a cursor you pass back in on the next call, mirroring the raw ?cursor=&limit= query parameters described in the OpenAPI spec walkthrough.

Configuring the client beyond the API key

The constructor accepts more than just apiKey in practice — expect options for things like a request timeout, a custom baseUrl (useful if you’re routing through an internal proxy), and a maxRetries count for transient network failures. None of these are required for a first call, but they’re worth setting explicitly once code moves past a prototype:

const client = new Norbelys({
  apiKey: process.env.NORBELYS_API_KEY,
  timeout: 10_000,
  maxRetries: 2,
});

A sensible retry count matters more than it might seem for anything that calls the send door — a transient network blip retried automatically is a non-event; the same blip left unhandled in application code is a support ticket. Just make sure retries are only applied to idempotent calls, or pair them with the Idempotency-Key header the API already supports on state-changing POST requests, so a retried create can’t double-send.

Using the client inside a Worker

Because Norbelys’s own control plane runs on Cloudflare Workers, the Node SDK is built to work the same way inside yours — no Node-only APIs it secretly depends on. Construct the client once per request (or once per isolate, if you’re caching it on globalThis), read the API key from your Worker’s secret bindings instead of process.env, and the rest of the code looks identical to any other Node environment:

export default {
  async fetch(request: Request, env: Env) {
    const client = new Norbelys({ apiKey: env.NORBELYS_API_KEY });
    const { data } = await client.people.list({ limit: 10 });
    return Response.json(data);
  },
};

This is also the shape you’d reach for if you’re building an integration that reacts to Norbelys data on a schedule rather than waiting on a push — see what to use instead of webhooks if that’s the integration you have in mind.

TypeScript strictness pays off here specifically

If your project runs with strict: true, the generated types will flag things a loosely-typed HTTP client never would: a field that’s optional on create but required on update, a union type on status that doesn’t include a value you assumed existed, an enum that changed shape between API versions. None of that is the SDK being fussy — it’s the same validation the API would perform server-side, surfaced at compile time instead of as a runtime 422.

Where this fits with everything else

The SDK, the CLI, and the MCP server are three different front doors onto the exact same contract — none of them can drift from the real API because all four are generated from one OpenAPI spec. If you’d rather work from a terminal than write Node, the CLI covers the same ground; if you’re wiring up an AI agent instead of a script, MCP is the better fit. Pick based on where the calling code lives, not because one is “more official” than another.

If you’re not sure Node is even the right language for your integration — maybe this is a data pipeline, or a Rails app, or a Cloudflare Worker — the SDK comparison guide walks through when Python, Go, or Ruby is the better call.

Next steps