Skip to main content
The server-side API is HTTP from your backend, authenticated with a secret (sk_…) key. Use it to register wallets as contacts, ingest form submissions, send pushes, and launch campaigns, everything that reads or writes data.
Secret keys are server-only. They grant full server-to-server access to your workspace, so never ship one to a browser, a mobile app, a repo, or any client. Client code uses a publishable (pk_…) key, which can only run the wallet auth handshake. If a secret key is ever exposed, roll it with zero grace immediately.

Authentication

Every call carries your secret key in a header. Two forms are accepted, use whichever your HTTP client makes easy:
or
The organization is derived from the key, you never pass an org id. Base path:
A sk_test_… key runs against your workspace in test mode, which is the safe way to integrate before going live, custom events sent with a test key are dry-run, for example. Read-only keys reject writes with 403. See API keys for scopes, rolling, and revocation.

Quickstart: ingest a capture

The highest-value first call for most backends: hand a consented email (and optional wallet) to a capture form from your server. If the form uses double opt-in, this kicks off the confirmation email; on confirmation the contact joins the bound list and can fire an automation.
The key’s organization must own the form ({token} is the form’s token), or the request 404s. See capture forms for the body fields and the confirmation flow.

The server-to-server surface

A secret key unlocks a small, deliberate set of endpoints. Reads never expose the email address itself.
Email can’t be set through POST /identify, by design. The only consent-first path for an address is a capture form, embedded or ingested server-side with the endpoint above.
A few semantics worth knowing before you wire these up:
  • POST /identify is idempotent on the wallet, lower-cases the address, and rejects any personal identifier (email, phone, name) with 400 PERSONAL_IDENTIFIER_REJECTED. To attach traits, send a custom event instead.
  • POST /campaigns/{id}/send takes a prepared campaign id, not the campaignRunId returned by /inapp/push. It can’t compose or retarget; it launches what’s already in the dashboard.
  • GET /events/catalog is a dashboard (session) route, not a secret-key one, so a sk_ key gets 401 there.

Wrap it once

Keep the secret key in one server-only module and expose your own thin routes, so the key never reaches the browser.
Call it from your own endpoint so the browser only ever talks to your server:

The shape of a backend integration

Register wallets as contacts, collect email through a form, with consent, then send product events and let campaigns and automations operate on the result. To hear about what happens next, delivered, viewed, unsubscribed, register a webhook.

Errors

Responses use the standard envelope. Common statuses: