> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sendrealm.com/llms.txt
> Use this file to discover all available pages before exploring further.

# JavaScript SDK

> Use the JavaScript and TypeScript SDK from trusted backend code.

Use the Sendrealm JavaScript SDK from trusted server-side or edge environments to call the Sendrealm API.

This SDK uses a Sendrealm API key. Do not use it to register browsers or mobile devices for push. Use the Android, iOS, React Native, or React Web Push SDK for device registration.

<Warning>
  Do not expose a Sendrealm API key in browser JavaScript or mobile app code. API keys are server-side secrets.
</Warning>

## Runtime Support

The SDK uses native `fetch` and has no runtime dependencies.

* Node.js 18 or newer.
* Cloudflare Workers.
* Deno.
* Bun.
* Vercel Edge Runtime.
* Other runtimes with `fetch`, `Headers`, `Request`, and `Response`.

## Install

```bash theme={null}
npm install @sendrealm/sdk
```

## Create An API Key

Create an API key in the Sendrealm dashboard and store it in your secret manager or environment variables.

```bash theme={null}
SENDREALM_API_KEY=sk_...
```

See [API Keys](/tutorials/api-keys) for dashboard setup.

## Create A Client

```ts theme={null}
import Sendrealm from "@sendrealm/sdk";

const client = new Sendrealm({
  apiKey: process.env.SENDREALM_API_KEY,
  projectId: process.env.SENDREALM_PROJECT_ID,
});
```

If `SENDREALM_API_KEY` is set, the client can read it automatically:

```ts theme={null}
import Sendrealm from "@sendrealm/sdk";

const client = new Sendrealm();
```

## Client Options

Use client options for custom timeouts, retries, headers, or fetch behavior:

```ts theme={null}
const client = new Sendrealm({
  apiKey: process.env.SENDREALM_API_KEY,
  timeout: 60_000,
  maxRetries: 2,
  fetch: customFetch,
  defaultHeaders: {
    "X-App": "my-service",
  },
});
```

Important options:

* `apiKey`: required unless `SENDREALM_API_KEY` is set.
* `projectId`: optional project selector; defaults to `SENDREALM_PROJECT_ID`, then to the API key's default project.
* `timeout`: request timeout in milliseconds.
* `maxRetries`: number of automatic retries for retryable failures.
* `fetch`: custom fetch implementation.
* `defaultHeaders`: headers sent with every request.
* `defaultQuery`: query parameters sent with every request.

`apiKey` may also be an async function if your service rotates credentials.

## Send Email

```ts theme={null}
const email = await client.emails.send({
  from: "hello@example.com",
  to: ["user@example.com"],
  subject: "Welcome",
  text: "Hello from Sendrealm",
  html: "<p>Hello from Sendrealm</p>",
});
```

Before sending production email, make sure the sending domain is verified in Sendrealm.

## Send Push

Send push notifications to registered devices:

```ts theme={null}
await client.push.notifications.send({
  app_id: "app_123",
  emails: ["user@example.com"],
  notification: {
    title: "Your order shipped",
    body: "Track it now",
    launch_url: "https://example.com/orders/123",
  },
});
```

Push sending requires device setup first:

* Android devices use the Android or React Native SDK.
* iOS devices use the iOS or React Native SDK.
* Web browsers use the React Web Push SDK.
* Firebase, APNs, or Web Push provider settings must be configured in Sendrealm.

## Test And Diagnose Push

Use API-key resources from trusted backend code to inspect setup and prove one
device end to end:

```ts theme={null}
const apps = await client.push.apps.list();
const app = apps[0];
const providers = await client.push.apps.listProviders(app.public_id);

const devices = await client.push.devices.list({
  app_id: app.public_id,
  environment: "development",
  subscribed: true,
});

const test = await client.push.tests.create({
  app_id: app.public_id,
  device_id: devices.data[0].device_id,
  title: "Sendrealm test",
  body: "Push registration is working",
});

const trace = await client.push.tests.retrieve(test.id);
```

Raw APNs, FCM, and Web Push credentials are not returned by app or device
resources. Push campaign SDK methods create and edit drafts, preview reachable
devices, and test a draft on one device; they do not schedule or launch it.

## Ingest Events

```ts theme={null}
await client.events.ingest({
  event: "order.completed",
  email: "user@example.com",
  payload: {
    order_id: "order_123",
    total: 42,
  },
  idempotency_key: "order_123_completed",
});
```

Each event should include one identity, such as `contact_id`, `external_id`, or `email`.

Use an `idempotency_key` when your system might retry the same event.

For correlated journeys such as checkout recovery, pass the business identifier
on every related event:

```ts theme={null}
await client.events.ingest({
  event: "checkout.started",
  email: "user@example.com",
  correlation: { key: "checkout_id", value: "checkout_123" },
  contact: { mode: "upsert", fields: { first_name: "Olivia" } },
});
```

Avoid putting sensitive personal data in event data unless your team intentionally wants that data in Sendrealm.

## Work With Contacts

List contacts with cursor pagination:

```ts theme={null}
for await (const contact of client.contacts.list({ limit: 100 })) {
  console.log(contact.email);
}
```

Create a contact:

```ts theme={null}
const contact = await client.contacts.create({
  email: "user@example.com",
  fields: {
    first_name: "Olivia",
    plan: "pro",
  },
});
```

Update a contact:

```ts theme={null}
await client.contacts.update(contact.id, {
  fields: {
    plan: "enterprise",
  },
});
```

Look up or upsert a contact by email:

```ts theme={null}
const existing = await client.contacts.getByEmail("user@example.com");
const contact = await client.contacts.upsert({
  email: "user@example.com",
  fields: { plan: "enterprise" },
});
```

Use backend-owned contact fields for authoritative customer data such as billing plan, account status, compliance flags, and verified profile data.

## Dashboard Resources

Use API-key-scoped resources to inspect the current project, choose a verified domain, and create campaign drafts:

```ts theme={null}
const project = await client.project.retrieve();
const domains = await client.domains.list();
const verifiedDomain = domains.data.find(domain => domain.status === "verified");

if (!verifiedDomain) {
  throw new Error("A verified sending domain is required.");
}

const campaign = await client.campaigns.create({
  name: "July product update",
});

await client.campaigns.update(campaign.id, {
  subject: "What changed in July",
  from: {
    name: "Sendrealm",
    address: `hello@${verifiedDomain?.name}`,
  },
  audience_ids: ["018f4a28-29f0-7a2e-86c3-6a8f1b6dd294"],
  content: {
    type: "doc",
    content: [],
  },
});
```

Campaign APIs create and update drafts only. Review readiness and schedule or send the campaign from the SendRealm dashboard.

## Automations

Validate drafts before publishing, inspect runs, and use no-send test runs for
waits and timeout paths:

```ts theme={null}
const validation = await client.automations.validate(definition);

const testRun = await client.automations.tests.create(automationId, {
  event: "checkout.started",
  email: "user@example.com",
  correlation: { key: "checkout_id", value: "checkout_123" },
});

await client.automations.tests.advance(testRun.id, 3600);
await client.automations.runs.cancel(runId, {
  reason: "Customer completed checkout elsewhere",
});
```

## Pagination

Some list endpoints return cursor pages. Consume every item with `for await`:

```ts theme={null}
for await (const contact of client.contacts.list({ limit: 100 })) {
  console.log(contact.email);
}
```

Or inspect pages manually:

```ts theme={null}
let page = await client.contacts.list({ limit: 20 });

while (page.hasNextPage()) {
  page = await page.getNextPage();
}
```

## Errors

Non-2xx responses throw typed errors:

```ts theme={null}
import Sendrealm, { APIError, RateLimitError } from "@sendrealm/sdk";

try {
  await client.contacts.retrieve("missing");
} catch (error) {
  if (error instanceof RateLimitError) {
    console.log(error.headers.get("retry-after"));
  }

  if (error instanceof APIError) {
    console.log(error.status, error.code, error.message);
  }
}
```

Common error categories:

* Authentication errors: check the API key.
* Permission errors: check key scope and project access.
* Validation errors: check required fields and request shape.
* Rate limits: retry after the server-provided delay when available.
* Connection errors: check network and runtime fetch behavior.

## Edge Runtime Examples

Cloudflare Workers:

```ts theme={null}
import Sendrealm from "@sendrealm/sdk";

export default {
  async fetch(_request: Request, env: Env) {
    const client = new Sendrealm({
      apiKey: env.SENDREALM_API_KEY,
    });

    await client.events.ingest({
      event: "worker.received",
      external_id: "visitor_123",
    });

    return new Response("ok");
  },
};
```

Deno:

```ts theme={null}
import Sendrealm from "npm:@sendrealm/sdk";

const client = new Sendrealm({
  apiKey: Deno.env.get("SENDREALM_API_KEY"),
});
```

Bun:

```ts theme={null}
import Sendrealm from "@sendrealm/sdk";

const client = new Sendrealm({
  apiKey: Bun.env.SENDREALM_API_KEY,
});
```

## Production Checklist

* API key is stored in a secret manager or environment variable.
* API key is never sent to browser or mobile code.
* Sending domains are verified for email.
* Device SDKs are installed before backend push sends target app users.
* Event names are stable.
* Event submissions use `idempotency_key` where retries are possible.
* Errors are logged with status, code, and message.
* Rate limit responses are handled.

## Related Pages

* [API Keys](/tutorials/api-keys)
* [Events](/tutorials/events)
* [Send Push Notification API](/api-reference/endpoint/send-push-notification)
