# Contacts Use the Contacts API to add or update a contact for your organization. The primary identifier is **email**: if a contact with that email already exists, attributes are merged into their stored properties (upsert). ## SDK method [#sdk-method] ```ts noketa.contacts.create(payload: CreateContactRequest): Promise ``` ## HTTP [#http] `POST /api/v1/contacts` (legacy alias: `POST /api/v1/profiles`) Authenticate with the `Noketa-Secret` header set to your organization API key, or with a valid session cookie when calling from the browser. ## Request body [#request-body] | Field | Type | Required | Description | | ------------ | -------------------- | -------- | --------------------------------------------------------------------------- | | `email` | `string` | Yes | Contact email (normalized to lowercase) | | `attributes` | `ContactAttributes?` | No | Standard and custom fields (merged into `properties` on the contact) | | `subscribed` | `boolean?` | No | Only used when **inserting** a new contact (default `true` if omitted) | | `tags` | `string[]?` | No | Only used when **inserting**; non-empty strings, max 100 chars each, max 50 | `ContactAttributes` can include known optional fields and any custom keys. See [Type Reference](/docs/reference/types) for the full shape. ## Response [#response] ```json { "message": "Contact created", "contactId": "", "action": "created" } ``` On update, `message` is `"Contact updated"` and `action` is `"updated"`. ## Example (SDK) [#example-sdk] ```ts import { Noketa } from "noketa"; const noketa = new Noketa(process.env.NOKETA_API_KEY!); const response = await noketa.contacts.create({ email: "person@example.com", attributes: { first_name: "Jane", last_name: "Doe", locale: "en", external_id: "usr_123", properties: { plan: "pro", signup_source: "landing_page", }, }, }); ``` ## Notes [#notes] * Use `attributes` for both standard fields and custom data. Custom keys are supported on the object and under `properties`. * Pass dates (e.g. `birthdate`, `last_event_date`) as `Date` instances; they are serialized correctly for the API. * The `profiles` namespace on the SDK is deprecated and forwards to `contacts`. # Emails Use the Emails API to send **transactional** email (one-off sends from your app). Marketing **broadcasts** are sent from the Noketa app and are not covered by this endpoint. Transactional sends include Noketa **open** and **click** tracking by default. You may optionally set `unsubscribeUrl` to add a `List-Unsubscribe` header pointing at your own HTTPS endpoint (similar to [Resend’s transactional unsubscribe guidance](https://resend.com/docs/dashboard/emails/add-unsubscribe-to-transactional-emails)). ## Method [#method] ```ts noketa.emails.send(payload: SendEmailRequest): Promise ``` ## Request [#request] | Field | Type | Description | | --------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `from` | `string` | Sender address (domain must be verified in your account) | | `to` | `string \| string[]` | Recipient email address or addresses | | `subject` | `string` | Email subject line | | `html` | `string` | Optional HTML body (full document or fragment). If omitted, Noketa generates a trackable HTML alternative from `text`. | | `text` | `string` | Optional plain-text part | | `replyTo` | `string` | Optional reply-to address | | `tracking` | `boolean` | Optional. Defaults to `true`; set `false` to disable Noketa open/click tracking. | | `unsubscribeUrl` | `string` | Optional absolute `https` URL for the `List-Unsubscribe` header | | `unsubscribeOneClick` | `boolean` | Optional. When `true`, Noketa adds `List-Unsubscribe-Post: List-Unsubscribe=One-Click`. Your `unsubscribeUrl` must accept `POST` per RFC 8058. Requires `unsubscribeUrl`. | ## Example [#example] ```ts import { Noketa } from "noketa"; const noketa = new Noketa(process.env.NOKETA_API_KEY!); const response = await noketa.emails.send({ from: "noreply@yourdomain.com", to: "person@example.com", subject: "Welcome to Noketa", html: "

Welcome

Thanks for signing up.

", }); ``` ## Tracking [#tracking] By default, Noketa rewrites links for click tracking and adds an open pixel to the HTML part. That lets you see opens and clicks in the Noketa dashboard. On Pro and Enterprise, verified sending domains can use a branded tracking host such as `https://click.yourdomain.com`. While that host is not active, Noketa automatically falls back to `https://click.noketa.io` so sends are not blocked. Pass `tracking: false` to send without Noketa engagement tracking—your HTML and links are delivered unchanged (no rewrite, no pixel). ### When to disable tracking [#when-to-disable-tracking] Turn tracking off for sensitive or security-critical mail where you want links to go directly to your app and avoid loading tracking assets: * **Password resets** and account-recovery flows * **Magic links** and one-time sign-in URLs * **MFA, OTP, and verification codes** * Other **high-trust transactional** messages where engagement metrics are not useful These sends are usually one-off and action-oriented; open/click stats rarely inform product decisions the way they do for marketing email. ### Example (tracking disabled) [#example-tracking-disabled] ```ts await noketa.emails.send({ from: "noreply@yourdomain.com", to: user.email, subject: "Reset your password", html: `

Reset password

`, tracking: false, }); ``` ## Notes [#notes] * `html` can be a complete HTML document or a fragment; the API accepts both. * When only `text` is supplied, Noketa sends it as the text part and generates a minimal HTML part for open/click tracking. * Failed requests throw an `Error` with the message from the API; handle errors in try/catch. * For high-volume marketing sends, use **Broadcasts** in the Noketa app instead of looping this endpoint. * If `unsubscribeUrl` is invalid or not `https`, the API returns `400` with an error message. # Events and Automations Use the Events API to record product or lifecycle activity from your app. Events can start published automations and resume “Wait for event” steps for the matching contact. ## SDK method [#sdk-method] ```ts noketa.events.track(payload: TrackEventRequest): Promise ``` ## HTTP [#http] `POST /api/v1/events` Authenticate with the `Noketa-Secret` header set to your organization API key, or with a valid session cookie when calling from the browser. ## Request body [#request-body] | Field | Type | Required | Description | | ------------ | -------------------------- | -------- | ---------------------------------------------------------- | | `event` | `string` | Yes | Event key, e.g. `product.purchased` | | `email` | `string?` | No | Contact email. Used to find or create the contact | | `contactId` | `string?` | No | Existing Noketa contact ID. Takes precedence over `email` | | `attributes` | `ContactAttributes?` | No | Merged into the contact when `email` is used | | `metadata` | `Record?` | No | Stored on the event and passed to matching automation runs | Provide either `contactId` or `email`. When only `email` is provided, Noketa upserts the contact before tracking the event. If that creates a new contact, `contact.created` automations also run. ## Response [#response] ```json { "message": "Event tracked", "eventId": "", "contactId": "", "contactAction": "matched", "triggered": 1, "completedWaitpoints": 0, "failed": 0 } ``` ## Example [#example] ```ts import { Noketa } from "noketa"; const noketa = new Noketa(process.env.NOKETA_API_KEY!); await noketa.events.track({ event: "product.purchased", email: "person@example.com", attributes: { first_name: "Jane", properties: { plan: "pro", }, }, metadata: { orderId: "order_123", amount: 4900, }, }); ``` ## Event keys [#event-keys] Event keys may contain letters, numbers, dots, underscores, and hyphens. Built-in keys include `contact.created`; custom keys are automatically added to your organization’s automation event list when tracked. # Comparison Noketa is built for teams that want one platform for transactional email, marketing, and inbound aliases, with a single SDK and one billing story. ## Versus generic transactional email [#versus-generic-transactional-email] Many providers (SendGrid, Mailgun, Postmark, etc.) focus on transactional email only. Noketa gives you: * **Contacts and attributes** — Sync contacts and custom fields so you can target and personalize campaigns without a separate CRM or ESP. * **Same API for campaigns** — Use `emails.send` for both one-off and bulk; no separate “marketing” product or API. * **Inbound aliases** — Receive at addresses you control (e.g. `reply@yourdomain.com`) and forward or store mail; no need to wire a separate inbound service. ## Versus marketing-only ESPs [#versus-marketing-only-esps] Marketing platforms (Mailchimp, Klaviyo, etc.) are built around campaigns and automation. Noketa gives you: * **Developer-first API** — Install the SDK, create a client, and send; no UI-heavy flows for simple sends. * **Transactional and marketing in one** — One send API, one set of types, one place to manage deliverability and reputation. * **TypeScript and types** — First-class types and clear request/response shapes so your editor and tests stay in sync with the API. ## Summary [#summary] Use Noketa when you want a single email platform and SDK for transactional email, marketing, contacts, and (optionally) inbound aliases, with straightforward billing and type-safe integration. # Introduction Noketa is an email platform that unifies transactional email, marketing campaigns, and inbound alias management. Use the **Noketa JavaScript/TypeScript SDK** to integrate everything from a single API. ## What you can do [#what-you-can-do] * **Contacts** — Create and update contacts with the [Contacts API](/docs/concepts/contacts). Use standard and custom attributes, external IDs, and merge-on-update behavior (upsert by email). * **Events and automations** — Track custom events with the [Events API](/docs/concepts/events) to start and resume published workflows. * **Transactional & campaign email** — Send one-off or bulk emails with the [Emails API](/docs/concepts/emails). One API for both use cases; types and webhooks keep your code type-safe. * **Inbound aliases** — Manage addresses that receive mail and forward or store it (handled in the Noketa dashboard; the SDK focuses on sending and contacts). ## Next steps [#next-steps] * [Comparison](/docs/getting-started/comparison) — How Noketa compares to other providers * [Installation](/docs/getting-started/installation) — Install the SDK and send your first request * [Contacts](/docs/concepts/contacts), [Events](/docs/concepts/events), and [Emails](/docs/concepts/emails) — Core concepts and API reference # Installation This guide walks you through installing the Noketa SDK, creating a Noketa instance, and making your first API calls. ## Installation [#installation] Install the package with Bun: ```bash bun add noketa ``` ## Create a Noketa instance [#create-a-noketa-instance] Import the SDK and create a Noketa instance with your API key. The key is required; the constructor will throw if it is missing. ```ts import { Noketa } from "noketa"; const noketa = new Noketa(process.env.NOKETA_API_KEY!); ``` The client defaults to `https://api.noketa.io` as the API origin (paths under `/api/v1`). Override with `new Noketa(key, { baseUrl: "https://api.noketa.dev" })` when calling a local deployment. Keep your API key in environment variables and never commit it to version control. ## Your first request [#your-first-request] ### Create or update a contact [#create-or-update-a-contact] Sync a contact with the [Contacts API](/docs/concepts/contacts) (upsert by email): ```ts await noketa.contacts.create({ email: "person@example.com", attributes: { first_name: "Jane", last_name: "Doe", }, }); ``` ### Send an email [#send-an-email] Send a transactional or one-off email with the [Emails API](/docs/concepts/emails): ```ts await noketa.emails.send({ from: "noreply@yourdomain.com", to: "person@example.com", subject: "Welcome", html: "

Hello from Noketa

", }); ``` ### Track an event [#track-an-event] Track product or lifecycle activity with the [Events API](/docs/concepts/events) to start or resume automations: ```ts await noketa.events.track({ event: "product.purchased", email: "person@example.com", metadata: { orderId: "order_123", }, }); ``` ## Error handling [#error-handling] When the API returns an error, the SDK throws an `Error` with a message from the response. Always wrap calls in try/catch for production code. ```ts try { await noketa.emails.send({ from: "noreply@yourdomain.com", to: "person@example.com", subject: "Welcome", html: "

Hello from Noketa

", }); } catch (error) { console.error((error as Error).message); } ``` ## Next steps [#next-steps] * [Contacts](/docs/concepts/contacts) — Full reference for creating and updating contacts * [Events and Automations](/docs/concepts/events) — Track custom events for workflows * [Emails](/docs/concepts/emails) — Send options and response shape * [Type Reference](/docs/reference/types) — TypeScript types exported from the package # Types The `noketa` package exports the client and all request/response types from the root module. Use them for type-safe requests and responses. ## Exports [#exports] ```ts import { Noketa, type ContactAttributes, type CreateContactRequest, type CreateContactResponse, type NoketaApiResponse, type SendEmailRequest, type SendEmailResponse, type TrackEventRequest, type TrackEventResponse, } from "noketa"; ``` Legacy names `ProfileAttributes`, `CreateProfileRequest`, and `CreateProfileResponse` are still exported for compatibility; prefer the `Contact*` types. ## ContactAttributes [#contactattributes] Used in [Contacts](/docs/concepts/contacts) for `attributes`. Supports known optional fields and arbitrary custom keys: | Field | Type | Description | | ----------------- | -------------------------- | -------------------------------- | | `first_name` | `string?` | Given name | | `last_name` | `string?` | Family name | | `locale` | `string?` | Locale code (e.g. `en`, `en-US`) | | `gender` | `string?` | Gender | | `age` | `number?` | Age | | `birthdate` | `Date?` | Birth date | | `external_id` | `string?` | Your system’s user ID | | `last_event_date` | `Date?` | Last activity date | | `properties` | `Record?` | Custom key-value data | | `[key: string]` | `unknown` | Additional custom fields | ## API responses [#api-responses] `CreateContactResponse` is the typed shape returned by `contacts.create`: ```ts type CreateContactResponse = { message: string; contactId: string; action?: "created" | "updated"; }; ``` `SendEmailRequest` is used by [Emails](/docs/concepts/emails): ```ts type SendEmailRequest = { from: string; to: string | string[]; subject: string; html?: string; text?: string; replyTo?: string | string[]; /** Defaults to `true`. Set `false` to skip open/click tracking. */ tracking?: boolean; unsubscribeUrl?: string; unsubscribeOneClick?: boolean; }; ``` `SendEmailResponse` matches the public emails API: ```ts type SendEmailResponse = { messageId: string; }; ``` `NoketaApiResponse` remains available for loose response shapes when needed. `TrackEventRequest` is used by [Events and Automations](/docs/concepts/events): ```ts type TrackEventRequest = { event: string; email?: string; contactId?: string; attributes?: ContactAttributes; metadata?: Record; }; ``` `TrackEventResponse` describes the automation dispatch result: ```ts type TrackEventResponse = { message: string; eventId: string | null; contactId: string; contactAction: "matched" | "created" | "updated"; triggered: number; completedWaitpoints: number; failed?: number; }; ```