---
title: "SDK API"
description: "The SDK API at /api/v1/sdk and the workspace server API at /api/v1/public."
canonical: https://docs.notifiable.sh/public-api
source: https://docs.notifiable.sh/public-api.md
---

# SDK API

The SDK calls a versioned JSON API at `/api/v1/sdk`. A public key identifies the app and the environment. The client can load delivery configuration, save a device, read it back, update its push permission, list the notifications delivered to that subscriber, record one open for a delivered notification, report one click for a delivery, and record an event. A public key cannot send a notification. Sending and the rest of the product use a workspace server API key at `/api/v1/public`. An event can start an automation that sends to that subscriber when they are in the automation's segment.

## How it works

Routes for the client live under `/api/v1/sdk`. Every response uses one envelope: a request id, a status, a machine code, a message, a timestamp, and either `data` or `error`. The same request id is returned in `X-Request-Id`.

`GET /api/v1` confirms that version 1 is available and returns the SDK and public bases. It does not require a key. `GET /api/v1/sdk` confirms the SDK base.

`POST /api/v1/sdk/deep-links` stores the routes for that app. The SDK sends them when the app starts. A later list replaces the stored one. An empty list clears them. Either public key for the app writes the same list.

`GET /api/v1/sdk/client-config` returns the delivery configuration for that public key. Android includes a status and, when ready, the client options the SDK uses to initialize. It is omitted until a package name is saved. iOS includes the bundle ID once Apple is connected and a bundle ID is saved. Web includes the public key. Expo, and a cross-platform app with neither side saved, returns an empty object. A public key still cannot send.

Save, read, update, and the inbox require `X-Notifiable-Key`. The value is the app's development or production public key. That key chooses the environment. A missing, unknown, reset, or deleted-app key is rejected. The same device id in that app and environment updates the existing subscriber, so a retry does not create a second one. A granted permission stores the provider and token. A denied or not-yet-asked permission can be stored before a token exists. A new production subscriber at the workspace cap is rejected. Development is not capped. The first save turns notifications on only when permission is granted. A later permission change does not undo a dashboard unsubscribe. Save, read, and update include `prompt_permission`. It is true when the dashboard asked that device to be prompted again and permission is still not granted. Sending the same permission again leaves that request in place. Sending `prompted: true`, or a different permission, clears it. Reading a subscriber does not write.

The inbox lists delivery rows for one subscriber in that app and environment, newest first, 20 per page. Each row includes `opened` and `opened_at`. `opened=false` asks for delivered rows that have not been opened. A notification that was not delivered to that subscriber is not included. The other environment, another app, or a missing subscriber is rejected.

An open is recorded once for a delivery whose status is delivered. A failed, skipped, or pending delivery is rejected. Recording the same open again does not change the first time. The first open also stores one `notification_opened` event with that delivery id.

A click is one report of the delivery id from `opt` on that notification's link. The first report sets `clicked_at` and stores one `notification_clicked` event. A second report does not change the first time. A delivery that is not delivered, or that belongs to another app or environment, is rejected.

An event stores a name, the subscriber, and optional properties for that app and environment. It does not send by itself. A saved automation for the same event name sends only when that subscriber is in the automation's segment. `notifiable.record({ event, attribute })` sends `name` and `properties`. A string attribute is `{ value }`.

The reference is generated from the route declarations and rendered with Scalar at `/docs/api` in a local environment. The OpenAPI document is `/docs/api.json`. The sidebar groups SDK routes (version, client configuration, subscribers, the inbox, and events) apart from server routes (the workspace, its apps, and each app's environment) and agents routes (platform users).
