Skip to main content

Authentication & API Keys

The Hober REST API base URL is https://api.hober.io. Every request is authenticated with one of three credential types, each scoped to what that kind of caller should be able to do.

Credential types at a glance​

CredentialFormatSent asSecrecyTypical caller
Client SDK keysk_…X-SDK-Key headerPublishableMobile / browser SDKs embedded in your app
Server keyhober_srv_…Authorization: BearerSecretYour backend (event tracking, export, REST hooks)
Dashboard session tokenJWTAuthorization: BearerSecret, short-livedThe Hober dashboard on behalf of a logged-in user

Client SDK key​

The client SDK key is a publishable, workspace-scoped key designed to be shipped inside mobile apps and web pages. Its format is a sk_ prefix, an encoded workspace identifier, and an HMAC-SHA256 signature:

sk_XXXXXXXXXXXXXXXX.YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY

The signature is embedded in the key itself — requests are not individually signed. Send the key verbatim on every request:

X-SDK-Key: YOUR_SDK_KEY

Authorization: SdkKey YOUR_SDK_KEY is accepted too. The client-key routes answer CORS from any origin, so the browser SDK can call them from your own site.

You can copy your workspace's SDK key from the dashboard under Settings → API keys. The key is stable for the life of the workspace: the same key is returned every time you view it, and there is currently no self-serve rotation.

What the client key can call​

Because the key is extractable from a shipped app, it only grants write-forward, device-facing operations:

  • POST /api/v1/subscribers/upsert — create or update a subscriber
  • POST /api/v1/devices — register a device push token
  • POST /api/v1/notifications/interactions — record opens, taps, and clicks
  • POST /v1/events and POST /v1/events/identify — behavioral event ingestion (client tier — see Events for tier limits)
  • The in-app message delivery stream used by the SDKs

It cannot read or export data, send notifications, or manage any workspace resource. A missing or invalid key returns 401 with code UNAUTHORIZED.

Identity verification​

The client key proves which workspace is calling, not which user. To prove the user, your backend signs each user's external_id with your workspace's identity secret, and your app sends that signature as a header:

X-Hober-Identity: hex(HMAC-SHA256(identity_secret, external_id))

Compute it on your server, never in the app, and hand it to the app with the user's session. Each server SDK has a helper; without one, for example in Node.js:

const token = crypto.createHmac('sha256', IDENTITY_SECRET).update(user.id).digest('hex')

Generate the secret in the dashboard under Settings → Identity verification (owners and admins), or with POST /api/v1/account/identity-verification/rotate. It is shown once. The same screen switches the mode and shows, for the last 7 days, how many calls had no valid signature: what enforce would reject (GET /api/v1/registry/identity-report?days=7). Rotating again keeps the previous secret valid for 72 hours so you can redeploy. GET /api/v1/account/identity-verification shows the current mode and PUT with {"mode": "off" | "report" | "enforce"} changes it:

ModeWrites to an existing subscriber without a valid token
report (default)accepted and counted, so you can see what enforce would reject
enforcerejected; device registration and the in-app stream and inbox need the token too
offaccepted, not counted

The in-app stream and inbox (/v1/in-app/stream, /v1/in-app/messages) take the token for the subscriber in subscriber_id. Under enforce they refuse a call without it, because the client key alone would let anyone read another user's messages by their id. The SDKs' in-app clients send the token you passed to identify.

Events sent with the client key follow the same modes. Under enforce, an event naming a subscriber_id the token does not verify is detached from that subscriber: it is kept as an anonymous event when it carries an anonymous_id, and dropped otherwise. The request still returns 202, so it reaches neither the subscriber's profile nor their journeys. Linking an anonymous_id to a subscriber (POST /v1/events/identify) without a valid token returns 403. Server keys are trusted and never need the token.

In every mode, a call without a valid token cannot replace or clear an existing subscriber's email, and cannot record consent for an email the subscriber already has. The first is rejected; the consent is ignored. Creating a new subscriber, or setting the first email on one that has none, never needs a token. A rejected call returns 403 with code IDENTITY_VERIFICATION_REQUIRED.

If Hober briefly cannot check signatures, a workspace in enforce fails closed wherever Hober has already seen its mode (almost always): a write to an existing subscriber, a device registration and POST /v1/events/identify return 503 with code IDENTITY_VERIFICATION_UNAVAILABLE and a Retry-After header, rather than being accepted unverified. The Hober SDKs wait and retry these calls up to three times before reporting the error; if you call the API directly, retry after Retry-After. In report and off, the call goes through unverified.

Server keys​

Server keys are secret, revocable credentials for server-to-server calls. Their format is the hober_srv_ prefix followed by 32 URL-safe base64 characters. Send them as a bearer token:

Authorization: Bearer hober_srv_XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Create and revoke server keys in the dashboard under Settings → API keys (requires a role with credential-management permission). The full key is shown exactly once at creation — only its prefix is displayed afterwards, and the key is stored as a one-way hash.

Scopes​

Each server key carries a set of scopes with the shape domain:read / domain:write. Keys created without explicit scopes get the default set:

ScopeGrants
events:writeEvent ingestion (POST /v1/events, /v1/events/identify) — the default scope
subscribers:readSubscriber export (GET /v1/subscribers/export): every subscriber with email, consent status and attributes
subscribers:writeServer identify (POST /v1/subscribers/identify): create or update subscribers by external_id, claiming a subscription-form signup with the same email

A server key that reaches an endpoint without its scope receives 403 with code FORBIDDEN. Because subscribers:read exposes every subscriber's personal data, and subscribers:write can take over any form signup by email, only an owner or admin can create a key that carries either.

What server keys can call​

  • POST /v1/events, POST /v1/events/identify — server-tier event ingestion, including external_id actor resolution and larger batch limits
  • GET /v1/events/export — bulk event history export
  • GET /v1/subscribers/export — subscriber export for syncing systems (subscribers:read)
  • POST /v1/subscribers/identify — identify a signed-in user and claim their form signup (subscribers:write)
  • GET /v1/whoami — the identity of the presented key
  • POST /v1/hooks, GET /v1/hooks, DELETE /v1/hooks/:id — REST hook (webhook subscription) management, as used by the Zapier integration
  • GET /v1/subscribers — a paged subscriber listing for integrations

See the Server-Side Event Tracking guide for choosing between the client and server tier.

Revocation​

Revoking a key takes effect immediately for most surfaces; the event ingestion path caches successful validations briefly, so a revoked key can keep ingesting for up to about one minute.

Dashboard session tokens​

Management surfaces — notifications, campaigns, templates, schedules, segments, journeys, channels, webhooks, team, and billing — are authenticated with the short-lived JWT bearer tokens that the dashboard obtains when a user logs in. Access tokens expire after 15 minutes and are refreshed transparently by the dashboard session; role-based permissions on the logged-in user additionally gate what each token may do (for example, read-only tokens receive 403 with code READ_ONLY_TOKEN on write requests).

There is currently no long-lived personal access token for the management API. For programmatic server-to-server access, use a server key (events, export, and REST hooks) — or agent access over MCP, which uses its own hober_mcp_ key type; see the Agent access guide.