API Reference
# NoPeek — E2EE chat APIs your servers can't read.
Every channel is end-to-end encrypted by default using MLS (RFC 9420). NoPeek servers act as an MLS delivery service: they store, order, and fan out opaque ciphertext. The only readable fields are routing metadata (sender id, channel id, timestamps, MLS epoch) — documented per endpoint.
## Authentication Three credential classes, all sent as Authorization: Bearer <secret>: - Org admin key (npk_live_…/npk_test_…, role org_admin) — org and app management. Dashboard/self-service only. Never ship to a server that handles chat traffic, never to a client. - App server key (role app_server) — your backend uses this for user provisioning, channel administration, moderation, webhooks, exports. - Session token — short-lived per-end-user token your backend mints via POST /v1/apps/{appId}/users/{userId}/sessions, then hands to the client SDK. All client endpoints and the WebSocket use this.
## Environments Apps are live or test. Test-key traffic against live apps (and vice versa) fails with WRONG_ENVIRONMENT.
## E2EE rules - Channel types default e2ee: true. Opt-out is explicit, only at type-creation, and badged in the dashboard forever. - In E2EE channels, message.ciphertext is required and plaintext is rejected (E2EE_REQUIRED inverse); in opted-out channels the reverse. - Server-side content features (profanity filter, plaintext search, server-rendered link previews) exist ONLY for opted-out channel types.
API version 2026-07-01 · OpenAPI 3.1.0 · 83 operations
Examples use https://api.nopeekapi.com.
The base URL of the current deployment is injected at deploy time via config.js
(window.NOPEEK_API_URL); when set, the URLs on this page update automatically.
Orgs
Self-service signup and org management.
POST
/v1/orgs
No authentication
Sign up (create an org)
Self-service signup. Returns the org plus a one-time org_admin API key secret. Provisioning is instant — an org/app is data, not infrastructure.
Request body
Responses
201 Org created.
org Org
orgId string
name string
billingEmail string
plan string
One of: free, starter, pro, enterprise.
hipaaBaaSignedAt string (date-time) | null
createdAt string (date-time)
apiKey ApiKeyWithSecret
apiKeyId string
role string
One of: org_admin, app_server.
secretPrefix string
First characters for display: npk_live_ab…
label string
lastUsedAt string (date-time) | null
createdAt string (date-time)
secret string
Shown exactly once. Store it now.
409 Conflict (ALREADY_EXISTS, STALE_EPOCH, NO_KEY_PACKAGES…). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/orgs" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Inc.",
"billingEmail": "founders@acme.dev",
"password": "correct-horse-battery-st4ple"
}'
GET
/v1/orgs/me
Org admin key
Get the authenticated org
Responses
200 The org owning the presented key. Org
orgId string
name string
billingEmail string
plan string
One of: free, starter, pro, enterprise.
hipaaBaaSignedAt string (date-time) | null
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/orgs/me" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY"
Apps
Applications — one per product/environment; instant provisioning.
GET
/v1/apps
Org admin key
List apps
Responses
200 Apps in this org.
apps array of App
appId string
orgId string
name string
environment string
One of: live, test.
region string
webhookUrl string | null
webhookEvents array of string
createdAt string (date-time)
branding object | null
White-label branding (logo URL + colors). Readable by clients via GET /branding.
sessionTtlSeconds integer
Auto-logoff: session token lifetime. One of the allowed presets. One of: 172800, 604800, 2592000, 5184000, 7776000, 15552000.
appPolicy AppPolicy
Capacitor remote-load force-update policy. Clients poll GET /v1/app-config every ~30s; a change in refreshToken (or a min-version gate the running build fails) triggers the update prompt.
forceRefreshEnabled boolean
refreshToken string
Opaque token; when it changes, running clients reload.
minWebBuild string | null
minIosVersion string | null
minAndroidVersion string | null
title string
message string
iosUpdateUrl string | null
androidUpdateUrl string | null
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY"
POST
/v1/apps
Org admin key
Create an app
Requires an org_admin key. Also mints the app's first app_server key (returned once) and creates default channel types: direct, group, broadcast — all e2ee: true.
Request body
Responses
201 App created with defaults.
app App
appId string
orgId string
name string
environment string
One of: live, test.
region string
webhookUrl string | null
webhookEvents array of string
createdAt string (date-time)
branding object | null
White-label branding (logo URL + colors). Readable by clients via GET /branding.
sessionTtlSeconds integer
Auto-logoff: session token lifetime. One of the allowed presets. One of: 172800, 604800, 2592000, 5184000, 7776000, 15552000.
appPolicy AppPolicy
Capacitor remote-load force-update policy. Clients poll GET /v1/app-config every ~30s; a change in refreshToken (or a min-version gate the running build fails) triggers the update prompt.
forceRefreshEnabled boolean
refreshToken string
Opaque token; when it changes, running clients reload.
minWebBuild string | null
minIosVersion string | null
minAndroidVersion string | null
title string
message string
iosUpdateUrl string | null
androidUpdateUrl string | null
serverKey ApiKeyWithSecret
apiKeyId string
role string
One of: org_admin, app_server.
secretPrefix string
First characters for display: npk_live_ab…
label string
lastUsedAt string (date-time) | null
createdAt string (date-time)
secret string
Shown exactly once. Store it now.
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Acme Chat (test)",
"environment": "test",
"region": "us-east-1"
}'
GET
/v1/apps/{appId}
App server key
Get an app
Parameters
Responses
200 The app. App
appId string
orgId string
name string
environment string
One of: live, test.
region string
webhookUrl string | null
webhookEvents array of string
createdAt string (date-time)
branding object | null
White-label branding (logo URL + colors). Readable by clients via GET /branding.
sessionTtlSeconds integer
Auto-logoff: session token lifetime. One of the allowed presets. One of: 172800, 604800, 2592000, 5184000, 7776000, 15552000.
appPolicy AppPolicy
Capacitor remote-load force-update policy. Clients poll GET /v1/app-config every ~30s; a change in refreshToken (or a min-version gate the running build fails) triggers the update prompt.
forceRefreshEnabled boolean
refreshToken string
Opaque token; when it changes, running clients reload.
minWebBuild string | null
minIosVersion string | null
minAndroidVersion string | null
title string
message string
iosUpdateUrl string | null
androidUpdateUrl string | null
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
PATCH
/v1/apps/{appId}
App server key
Update app settings (name, webhook, push config)
Parameters
Request body (optional)
name string
webhookUrl string (uri) | null
webhookEvents array of string
e.g. ["message.new", "member.joined"]. Webhook payloads for E2EE channels contain metadata only.
branding object | null
White-label branding blob (logo URL + brand colors only).
appPolicy object | null
Per-app force-update policy overlay, merged over the global policy.
sessionTtlSeconds integer
Auto-logoff preset in seconds: 48h, 7d, 30d, 60d, 90d, 180d. Any other value → INVALID_REQUEST. One of: 172800, 604800, 2592000, 5184000, 7776000, 15552000.
Responses
200 Updated app. App
appId string
orgId string
name string
environment string
One of: live, test.
region string
webhookUrl string | null
webhookEvents array of string
createdAt string (date-time)
branding object | null
White-label branding (logo URL + colors). Readable by clients via GET /branding.
sessionTtlSeconds integer
Auto-logoff: session token lifetime. One of the allowed presets. One of: 172800, 604800, 2592000, 5184000, 7776000, 15552000.
appPolicy AppPolicy
Capacitor remote-load force-update policy. Clients poll GET /v1/app-config every ~30s; a change in refreshToken (or a min-version gate the running build fails) triggers the update prompt.
forceRefreshEnabled boolean
refreshToken string
Opaque token; when it changes, running clients reload.
minWebBuild string | null
minIosVersion string | null
minAndroidVersion string | null
title string
message string
iosUpdateUrl string | null
androidUpdateUrl string | null
cURL
curl -X PATCH "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"webhookUrl": "https://example.com/hooks/nopeek",
"webhookEvents": [
"message.new",
"member.joined",
"report.created"
]
}'
JavaScript (fetch)
const res = await fetch('https://api.nopeekapi.com/v1/apps/app_7f3kq2c9', {
method: 'PATCH',
headers: {
Authorization: 'Bearer npk_live_SERVER_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"webhookUrl": "https://example.com/hooks/nopeek",
"webhookEvents": [
"message.new",
"member.joined",
"report.created"
]
}),
});
const data = await res.json();
DELETE
/v1/apps/{appId}
Org admin key
Delete an app and all its data
cURL
curl -X DELETE "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY"
API Keys
GET
/v1/apps/{appId}/keys
Org admin key
List keys (prefixes only)
Parameters
Responses
200 Keys.
keys array of ApiKey
apiKeyId string
role string
One of: org_admin, app_server.
secretPrefix string
First characters for display: npk_live_ab…
label string
lastUsedAt string (date-time) | null
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/keys" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY"
POST
/v1/apps/{appId}/keys
Org admin key
Mint an app server key
Parameters
Request body (optional)
label string
Max length 120.
Responses
201 Key created; secret shown once. ApiKeyWithSecret
apiKeyId string
role string
One of: org_admin, app_server.
secretPrefix string
First characters for display: npk_live_ab…
label string
lastUsedAt string (date-time) | null
createdAt string (date-time)
secret string
Shown exactly once. Store it now.
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/keys" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"label": "backend-primary"
}'
DELETE
/v1/apps/{appId}/keys/{apiKeyId}
Org admin key
Revoke a key
cURL
curl -X DELETE "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/keys/key_3j7h9cx1" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY"
Users
GET
/v1/apps/{appId}/users
App server key
List users
Parameters
Responses
200 Page of users.
users array of User
nextCursor string | null
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users?limit=50" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/users
App server key
Create (or upsert by externalId) a user
Parameters
Request body
Responses
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"externalId": "user-1001",
"nickname": "Amina",
"metadata": {
"team": "support"
}
}'
JavaScript (fetch)
const res = await fetch('https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users', {
method: 'POST',
headers: {
Authorization: 'Bearer npk_live_SERVER_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"externalId": "user-1001",
"nickname": "Amina",
"metadata": {
"team": "support"
}
}),
});
const data = await res.json();
GET
/v1/apps/{appId}/users/{userId}
App server key
Get a user
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
PATCH
/v1/apps/{appId}/users/{userId}
App server key
Update a user
Parameters
Request body (optional)
nickname string
profileUrl string | null
metadata object
isActive boolean
Responses
cURL
curl -X PATCH "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"nickname": "Amina B.",
"isActive": true
}'
DELETE
/v1/apps/{appId}/users/{userId}
App server key
Delete a user (GDPR/right-to-erasure)
cURL
curl -X DELETE "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
Sessions
POST
/v1/apps/{appId}/users/{userId}/sessions
App server key
Mint a client session token
Your backend calls this, then hands the token to your client app. Default TTL 24h.
Parameters
Request body (optional)
Responses
201 Session token (shown once).
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/sessions" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"ttlSeconds": 86400
}'
JavaScript (fetch)
const res = await fetch('https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/sessions', {
method: 'POST',
headers: {
Authorization: 'Bearer npk_live_SERVER_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"ttlSeconds": 86400
}),
});
const data = await res.json();
Devices
Per-user devices; each holds its own MLS credential.
GET
/v1/apps/{appId}/users/{userId}/devices
App server key
List a user's devices
Parameters
Responses
200 Devices.
devices array of Device
deviceId string
userId string
credentialKey string (base64)
platform string
One of: web, ios, android, desktop, server.
displayName string
revokedAt string (date-time) | null
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/devices" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/users/{userId}/devices
Session token
Register a device (client)
Client-authenticated. Registers this device's MLS signature credential.
Parameters
Request body
credentialKey string (base64) required
Ed25519 public key, base64.
platform string required
One of: web, ios, android, desktop, server.
displayName string
Max length 120.
pushToken string | null
pushProvider string | null
One of: apns, fcm.
Responses
201 Device. Device
deviceId string
userId string
credentialKey string (base64)
platform string
One of: web, ios, android, desktop, server.
displayName string
revokedAt string (date-time) | null
createdAt string (date-time)
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/devices" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"credentialKey": "ZWQyNTUxOVB1YmxpY0tleQ==",
"platform": "web",
"displayName": "Amina’s laptop"
}'
JavaScript (fetch)
const res = await fetch('https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/devices', {
method: 'POST',
headers: {
Authorization: 'Bearer nps_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"credentialKey": "ZWQyNTUxOVB1YmxpY0tleQ==",
"platform": "web",
"displayName": "Amina’s laptop"
}),
});
const data = await res.json();
DELETE
/v1/apps/{appId}/users/{userId}/devices/{deviceId}
App server key
Revoke a device
Peers receive member.updated MLS proposals to remove this device from groups at next commit.
Parameters
Responses
cURL
curl -X DELETE "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/devices/dev_5r8s2mv6" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
Key Registry
MLS key packages — publish, count, claim. All payloads opaque.
GET
/v1/apps/{appId}/users/{userId}/devices/{deviceId}/key-packages
Session token
Count unclaimed key packages (client)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/devices/dev_5r8s2mv6/key-packages" \
-H "Authorization: Bearer nps_SESSION_TOKEN"
POST
/v1/apps/{appId}/users/{userId}/devices/{deviceId}/key-packages
Session token
Publish MLS key packages (client)
Devices keep a pool (recommend ≥ 20) of single-use key packages. Payloads are opaque to NoPeek.
Parameters
Request body
keyPackages array of object required
Max items 100.
cipherSuite integer required
data string (base64) required
dataHash string required
expiresAt string (date-time) required
Responses
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/devices/dev_5r8s2mv6/key-packages" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"keyPackages": [
{
"cipherSuite": 1,
"data": "a2V5UGFja2FnZUJsb2I=",
"dataHash": "sha256:9f2b1c44",
"expiresAt": "2026-10-01T00:00:00Z"
}
]
}'
POST
/v1/apps/{appId}/users/{userId}/key-packages/claim
Session token
Claim one key package per device of a user (client)
Called when adding userId to an E2EE channel. Atomically claims one unclaimed package per active device (each package is single-use). NO_KEY_PACKAGES if any device's pool is empty.
Parameters
Responses
200 One package per device.
keyPackages array of KeyPackage
409 Conflict (ALREADY_EXISTS, STALE_EPOCH, NO_KEY_PACKAGES…). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/key-packages/claim" \
-H "Authorization: Bearer nps_SESSION_TOKEN"
JavaScript (fetch)
const res = await fetch('https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/key-packages/claim', {
method: 'POST',
headers: {
Authorization: 'Bearer nps_SESSION_TOKEN',
},
});
const data = await res.json();
MLS Groups
Delivery-service endpoints for MLS handshake traffic.
GET
/v1/apps/{appId}/mls/groups/{mlsGroupId}/handshakes
Session token
Fetch handshake backlog since an epoch (client catch-up)
Parameters
Responses
200 Ordered handshake messages.
handshakes array of HandshakeMessage
mlsGroupId string required
epoch integer required
kind string required
One of: proposal, commit, welcome, group_info.
senderDeviceId string
recipientDeviceIds array of string
Welcome routing only.
payload string (base64) required
Opaque to NoPeek.
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/mls/groups/mlsg_2v6b8nt3/handshakes?sinceEpoch=41" \
-H "Authorization: Bearer nps_SESSION_TOKEN"
POST
/v1/apps/{appId}/mls/groups/{mlsGroupId}/handshakes
Session token
Submit MLS handshake traffic (client)
Commits are accepted only at the group's current epoch (single-writer rule); on success the epoch increments and the blob fans out to member devices over WS. Welcomes route only to recipientDeviceIds. Rejected commits return STALE_EPOCH.
Parameters
Request body HandshakeMessage
mlsGroupId string required
epoch integer required
kind string required
One of: proposal, commit, welcome, group_info.
senderDeviceId string
recipientDeviceIds array of string
Welcome routing only.
payload string (base64) required
Opaque to NoPeek.
createdAt string (date-time)
Responses
202 Accepted and fanned out.
409 Conflict (ALREADY_EXISTS, STALE_EPOCH, NO_KEY_PACKAGES…). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/mls/groups/mlsg_2v6b8nt3/handshakes" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mlsGroupId": "mlsg_2v6b8nt3",
"epoch": 42,
"kind": "commit",
"senderDeviceId": "dev_5r8s2mv6",
"payload": "bWxzLWhhbmRzaGFrZS1ibG9i"
}'
Channel Types
GET
/v1/apps/{appId}/channel-types
App server key
List channel types
Parameters
Responses
200 Types.
channelTypes array of ChannelType
key string required
Pattern ^[a-z0-9_-]{1,64}$.
name string required
kind string required
One of: direct, group, broadcast.
e2ee boolean
Default true.
editWindowSeconds integer | null
recallWindowSeconds integer | null
readReceipts boolean
Default true.
typingIndicators boolean
Default true.
reactions boolean
Default true.
threads boolean
Default true.
profanityFilter boolean
Opted-out (e2ee=false) types only. Default false.
maxMembers integer
Default 100.
channelTypeId string
appId string
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channel-types" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/channel-types
App server key
Create a channel type
e2ee defaults to true. Setting e2ee: false is the ONLY way any message plaintext ever reaches NoPeek, is allowed only at creation time, and is surfaced with a permanent badge in the dashboard.
Parameters
Request body ChannelTypeCreate
key string required
Pattern ^[a-z0-9_-]{1,64}$.
name string required
kind string required
One of: direct, group, broadcast.
e2ee boolean
Default true.
editWindowSeconds integer | null
recallWindowSeconds integer | null
readReceipts boolean
Default true.
typingIndicators boolean
Default true.
reactions boolean
Default true.
threads boolean
Default true.
profanityFilter boolean
Opted-out (e2ee=false) types only. Default false.
maxMembers integer
Default 100.
Responses
201 Channel type. ChannelType
key string required
Pattern ^[a-z0-9_-]{1,64}$.
name string required
kind string required
One of: direct, group, broadcast.
e2ee boolean
Default true.
editWindowSeconds integer | null
recallWindowSeconds integer | null
readReceipts boolean
Default true.
typingIndicators boolean
Default true.
reactions boolean
Default true.
threads boolean
Default true.
profanityFilter boolean
Opted-out (e2ee=false) types only. Default false.
maxMembers integer
Default 100.
channelTypeId string
appId string
createdAt string (date-time)
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channel-types" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"key": "support_dm",
"name": "Support DM",
"kind": "direct",
"editWindowSeconds": 900,
"recallWindowSeconds": 3600
}'
Channels
GET
/v1/apps/{appId}/channels
App server key
List channels (server key) / my channels (session token)
Parameters
Responses
200 Page of channels.
channels array of Channel
channelId string
appId string
channelTypeKey string
kind string
One of: direct, group, broadcast.
e2ee boolean
name string
coverUrl string | null
metadata object
isFrozen boolean
mlsGroupId string | null
memberCount integer
lastMessageAt string (date-time) | null
createdBy string | null
createdAt string (date-time)
nextCursor string | null
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels?limit=50" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/channels
App server key
Create a channel
For direct kinds the member set is distinct: re-creating with the same members returns the existing channel. For E2EE channels the creating device must then establish the MLS group (claim key packages, post Welcome). Callable with a session token (client) or server key.
Parameters
Request body
channelTypeKey string required
name string
Max length 200.
memberIds array of string
Max items 1000.
operatorIds array of string
metadata object
coverUrl string (uri) | null
Responses
201 Channel (or existing distinct channel — 200). Channel
channelId string
appId string
channelTypeKey string
kind string
One of: direct, group, broadcast.
e2ee boolean
name string
coverUrl string | null
metadata object
isFrozen boolean
mlsGroupId string | null
memberCount integer
lastMessageAt string (date-time) | null
createdBy string | null
createdAt string (date-time)
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"channelTypeKey": "direct",
"memberIds": [
"user_8m2n1xq4",
"user_3p9dq5w2"
]
}'
JavaScript (fetch)
const res = await fetch('https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels', {
method: 'POST',
headers: {
Authorization: 'Bearer npk_live_SERVER_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"channelTypeKey": "direct",
"memberIds": [
"user_8m2n1xq4",
"user_3p9dq5w2"
]
}),
});
const data = await res.json();
GET
/v1/apps/{appId}/channels/{channelId}
App server key
Get a channel
Parameters
Responses
200 Channel. Channel
channelId string
appId string
channelTypeKey string
kind string
One of: direct, group, broadcast.
e2ee boolean
name string
coverUrl string | null
metadata object
isFrozen boolean
mlsGroupId string | null
memberCount integer
lastMessageAt string (date-time) | null
createdBy string | null
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
PATCH
/v1/apps/{appId}/channels/{channelId}
App server key
Update channel (name, cover, metadata, freeze)
Parameters
Request body (optional)
name string
coverUrl string | null
metadata object
isFrozen boolean
Responses
200 Updated channel. Channel
channelId string
appId string
channelTypeKey string
kind string
One of: direct, group, broadcast.
e2ee boolean
name string
coverUrl string | null
metadata object
isFrozen boolean
mlsGroupId string | null
memberCount integer
lastMessageAt string (date-time) | null
createdBy string | null
createdAt string (date-time)
cURL
curl -X PATCH "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Design crew",
"isFrozen": false
}'
DELETE
/v1/apps/{appId}/channels/{channelId}
App server key
Delete a channel
cURL
curl -X DELETE "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
Members
GET
/v1/apps/{appId}/channels/{channelId}/members
App server key
List members
Parameters
Responses
200 Members.
members array of ChannelMember
channelId string
userId string
role string
One of: member, operator.
mutedUntil string (date-time) | null
bannedAt string (date-time) | null
lastDeliveredSeq integer
lastReadSeq integer
unreadCount integer
joinedAt string (date-time)
nextCursor string | null
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/members?limit=50" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/channels/{channelId}/members
App server key
Add members
For E2EE channels the caller (an existing member device or operator) must follow up with an MLS Add commit + Welcome; new members cannot decrypt until then.
Parameters
Request body
Responses
200 Updated member list.
members array of ChannelMember
channelId string
userId string
role string
One of: member, operator.
mutedUntil string (date-time) | null
bannedAt string (date-time) | null
lastDeliveredSeq integer
lastReadSeq integer
unreadCount integer
joinedAt string (date-time)
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/members" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"userIds": [
"user_6w2k9rt8"
]
}'
DELETE
/v1/apps/{appId}/channels/{channelId}/members/{userId}
App server key
Remove a member (or leave, with session token)
E2EE channels require a subsequent MLS Remove commit from a remaining member; until then the removed device is cryptographically excluded from the NEXT epoch, not past ones.
Parameters
Responses
cURL
curl -X DELETE "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/members/user_8m2n1xq4" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
Messages
GET
/v1/apps/{appId}/channels/{channelId}/messages
App server key
List messages (ciphertext envelopes in E2EE channels)
Parameters
Responses
200 Messages ordered by seq.
messages array of MessageEnvelope
messageId string
channelId string
senderUserId string
senderDeviceId string | null
seq integer
threadParentId string | null
mlsEpoch integer | null
ciphertext string (base64) | null
plaintext PlaintextBody | null
Opted-out channels only; E2EE channels reject it.
editedAt string (date-time) | null
recalledAt string (date-time) | null
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/messages?afterSeq=130&limit=50" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
JavaScript (fetch)
const res = await fetch('https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/messages?afterSeq=130&limit=50', {
method: 'GET',
headers: {
Authorization: 'Bearer npk_live_SERVER_KEY',
},
});
const data = await res.json();
POST
/v1/apps/{appId}/channels/{channelId}/messages
Session token
Send a message
E2EE channels: ciphertext (MLS application message) required; server assigns seq, stamps time, fans out over WS, dispatches push with an encrypted payload. Opted-out channels: plaintext required. clientMessageId makes the call idempotent per sender. Note: a server key can only send into opted-out channels (it holds no MLS credential) — bots in E2EE channels join as member devices.
Parameters
Request body
clientMessageId string required
Max length 64.
threadParentId string | null
mlsEpoch integer | null
ciphertext string (base64) | null
plaintext PlaintextBody | null
Opted-out channels only; E2EE channels reject it.
Responses
201 The authoritative envelope. MessageEnvelope
messageId string
channelId string
senderUserId string
senderDeviceId string | null
seq integer
threadParentId string | null
mlsEpoch integer | null
ciphertext string (base64) | null
plaintext PlaintextBody | null
Opted-out channels only; E2EE channels reject it.
editedAt string (date-time) | null
recalledAt string (date-time) | null
createdAt string (date-time)
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/messages" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"clientMessageId": "01J29ZQ8V2-0001",
"mlsEpoch": 42,
"ciphertext": "bWxzLWFwcGxpY2F0aW9uLW1lc3NhZ2U="
}'
JavaScript (fetch)
const res = await fetch('https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/messages', {
method: 'POST',
headers: {
Authorization: 'Bearer nps_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"clientMessageId": "01J29ZQ8V2-0001",
"mlsEpoch": 42,
"ciphertext": "bWxzLWFwcGxpY2F0aW9uLW1lc3NhZ2U="
}),
});
const data = await res.json();
GET
/v1/apps/{appId}/channels/{channelId}/messages/{messageId}
App server key
Get a message envelope
Parameters
Responses
200 Envelope. MessageEnvelope
messageId string
channelId string
senderUserId string
senderDeviceId string | null
seq integer
threadParentId string | null
mlsEpoch integer | null
ciphertext string (base64) | null
plaintext PlaintextBody | null
Opted-out channels only; E2EE channels reject it.
editedAt string (date-time) | null
recalledAt string (date-time) | null
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/messages/msg_9k2p4dr2" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
PATCH
/v1/apps/{appId}/channels/{channelId}/messages/{messageId}
Session token
Edit a message
Sender-only, within the channel type's editWindowSeconds. E2EE channels: supply new ciphertext (clients also embed an edit op in the E2EE payload so peers verify the edit cryptographically — the envelope swap alone is not trusted). Opted-out: new plaintext.
Parameters
Request body
Responses
200 Updated envelope. MessageEnvelope
messageId string
channelId string
senderUserId string
senderDeviceId string | null
seq integer
threadParentId string | null
mlsEpoch integer | null
ciphertext string (base64) | null
plaintext PlaintextBody | null
Opted-out channels only; E2EE channels reject it.
editedAt string (date-time) | null
recalledAt string (date-time) | null
createdAt string (date-time)
cURL
curl -X PATCH "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/messages/msg_9k2p4dr2" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"mlsEpoch": 42,
"ciphertext": "bmV3LWNpcGhlcnRleHQ="
}'
DELETE
/v1/apps/{appId}/channels/{channelId}/messages/{messageId}
App server key
Recall a message (delete for everyone)
Sender within recallWindowSeconds, or an operator/server key any time. Ciphertext is erased server-side; a tombstone (id, seq, recalledAt) remains for sync.
Parameters
Responses
cURL
curl -X DELETE "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/messages/msg_9k2p4dr2" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
GET
/v1/unfurl
App server key
Unfurl a link (preview)
Generate a rich preview for a URL — Open Graph metadata for websites, or an embeddable player descriptor for YouTube/Vimeo.
E2EE model: the *sender's* device calls this and embeds the returned preview inside the encrypted message body (as linkPreview), so the server only ever sees the URL at unfurl time — never the message. Recipients render the preview from the decrypted payload.
Security: authenticated (any valid key) and SSRF-guarded — only http/https, and hosts resolving to private, loopback, link-local or cloud-metadata ranges are refused. Rate-limited.
Parameters
Responses
400 Invalid or unsupported URL
401 Missing or invalid credentials
403 Host not allowed (SSRF guard)
cURL
curl -X GET "https://api.nopeekapi.com/v1/unfurl" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
Reactions
GET
/v1/apps/{appId}/channels/{channelId}/messages/{messageId}/reactions
App server key
List reactions (opted-out channels)
Parameters
Responses
200 Reactions grouped by emoji.
reactions array of Reaction
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/messages/msg_9k2p4dr2/reactions" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/channels/{channelId}/messages/{messageId}/reactions
Session token
Add a reaction (opted-out channels)
In E2EE channels reactions travel INSIDE ciphertext (a reaction payload) and are aggregated client-side; this endpoint returns E2EE_REQUIRED there.
Parameters
Request body
emoji string required
Max length 64.
Responses
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/messages/msg_9k2p4dr2/reactions" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"emoji": "👍"
}'
DELETE
/v1/apps/{appId}/channels/{channelId}/messages/{messageId}/reactions
Session token
Remove own reaction (opted-out channels)
cURL
curl -X DELETE "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/messages/msg_9k2p4dr2/reactions?emoji=%F0%9F%91%8D" \
-H "Authorization: Bearer nps_SESSION_TOKEN"
Receipts
GET
/v1/apps/{appId}/channels/{channelId}/receipts
App server key
Get per-member receipt high-water marks
Parameters
Responses
200 Member marks.
receipts array of object
userId string
lastDeliveredSeq integer
lastReadSeq integer
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/receipts" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/channels/{channelId}/receipts
Session token
Mark delivered/read up to a message (client)
High-water mark semantics; also available on the WebSocket (receipt.mark). Receipts are metadata and are visible to the server by design.
Parameters
Request body
Responses
204 Marked and fanned out.
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/receipts" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"kind": "read",
"messageId": "msg_9k2p4dr2"
}'
JavaScript (fetch)
const res = await fetch('https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/receipts', {
method: 'POST',
headers: {
Authorization: 'Bearer nps_SESSION_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"kind": "read",
"messageId": "msg_9k2p4dr2"
}),
});
if (res.status !== 204) throw new Error(`NoPeek error ${res.status}`);
Moderation
POST
/v1/apps/{appId}/channels/{channelId}/moderation/{action}
App server key
Ban/mute/freeze operations
Parameters
Request body (optional)
Responses
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/moderation/mute" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_3p9dq5w2",
"durationSeconds": 3600
}'
JavaScript (fetch)
const res = await fetch('https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/channels/chan_4t9wq1z7/moderation/mute', {
method: 'POST',
headers: {
Authorization: 'Bearer npk_live_SERVER_KEY',
'Content-Type': 'application/json',
},
body: JSON.stringify({
"userId": "user_3p9dq5w2",
"durationSeconds": 3600
}),
});
if (res.status !== 204) throw new Error(`NoPeek error ${res.status}`);
GET
/v1/apps/{appId}/reports
App server key
List reports (server key)
Parameters
Responses
200 Reports.
reports array of Report
id integer
channelId string
messageId string | null
reporterUserId string
reportedUserId string | null
category string
disclosedContent string | null
status string
One of: open, resolved, dismissed.
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/reports?status=open" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/reports
Session token
Report a message/user (client)
The E2EE moderation path: the REPORTER's device decrypts the offending message locally and voluntarily discloses that plaintext with the report. NoPeek never breaks encryption — disclosure is a member exercising their own right to the content they can already read.
Parameters
Request body
channelId string required
messageId string | null
reportedUserId string | null
category string required
One of: spam, harassment, illegal, self_harm, other.
disclosedContent string | null
Max length 20000.
Responses
201 Report filed; webhook report.created fires.
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/reports" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"channelId": "chan_4t9wq1z7",
"messageId": "msg_9k2p4dr2",
"reportedUserId": "user_3p9dq5w2",
"category": "harassment",
"disclosedContent": "decrypted text, voluntarily disclosed by the reporter"
}'
History Backups
Encrypted, client-keyed history archives for new-device sync.
GET
/v1/apps/{appId}/users/{userId}/history-backups
Session token
List backups (newest first) with download URLs (client)
Parameters
Responses
200 Backups.
backups array of HistoryBackup
backupId string
userId string
deviceId string
keyDerivation object
scheme string
salt string (base64)
memoryKib integer
iterations integer
blobKey string
sizeBytes integer
manifestHash string
downloadUrl string | null
Pre-signed GET when listed by the owner.
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/history-backups" \
-H "Authorization: Bearer nps_SESSION_TOKEN"
POST
/v1/apps/{appId}/users/{userId}/history-backups
Session token
Register an encrypted history backup (client)
Returns a pre-signed PUT URL for the encrypted archive. The backup key is derived on-device from the user's recovery code (argon2id → HKDF); NoPeek stores derivation parameters, never key material.
Parameters
Request body
Responses
201 Backup slot with upload URL.
backup HistoryBackup
backupId string
userId string
deviceId string
keyDerivation object
scheme string
salt string (base64)
memoryKib integer
iterations integer
blobKey string
sizeBytes integer
manifestHash string
downloadUrl string | null
Pre-signed GET when listed by the owner.
createdAt string (date-time)
uploadUrl string (uri)
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/users/user_8m2n1xq4/history-backups" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"keyDerivation": {
"scheme": "argon2id-hkdf-v1",
"salt": "c2FsdEJ5dGVz",
"memoryKib": 65536,
"iterations": 3
},
"sizeBytes": 1048576,
"manifestHash": "sha256:9f2b1c44"
}'
Uploads
Pre-signed upload slots for client-side-encrypted attachments.
POST
/v1/apps/{appId}/uploads
Session token
Get a pre-signed slot for an encrypted attachment (client)
Clients encrypt attachments with a fresh AES-256-GCM content key BEFORE upload; the key travels only inside message ciphertext. The blob NoPeek stores is unreadable. Used by all rich content that carries a file: images, video, audio, generic files, and contact/thumbnail images. The SDK exposes this as np.uploadEncrypted(bytes) / channel.sendAttachment(...); structured rich types (poll, contact, calendar_event, location) need no upload — they ride inside the message payload. See the RichMessagePayload schema for the on-the-wire format every SDK agrees on (the server never parses it in E2EE channels).
Parameters
Request body
Responses
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/uploads" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"sizeBytes": 1048576
}'
Messenger
White-label messenger: turnkey end-user auth (username/password or Cognito), org/referral join codes, and the unified multi-org inbox. No public registration.
POST
/v1/apps/{appId}/messenger/login
No authentication
Log in with username + password
Turnkey end-user login: the customer needs no backend. Returns a NoPeek session token, the user profile, their workspace memberships, and whether a forced password change / recovery-key save is still pending. Failed attempts are audit-logged.
Parameters
Request body
username string required
password string required
Responses
200 Session + profile + workspaces. Session
sessionToken string
Short-lived client credential (nps_…). Hand to the SDK; TTL follows the app’s sessionTtlSeconds.
expiresAt string (date-time)
user MessengerUser
End-user profile for the white-label messenger. Username is typically the email (admins add users by email); the display handle is separate.
userId string
username string
email string | null
nickname string | null
avatarUrl string | null
statusText string | null
mustChangePassword boolean
True until the user completes their forced first-login password change.
hasRecoveryKey boolean
Whether a recovery-key hint has been stored for this account.
lastSeenAt string (date-time) | null
workspaces array of Workspace
workspaceId string
appId string
name string
slug string | null
branding object | null
myRole string | null
Present only when fetched with a session token: the caller’s role. One of: member, admin, owner.
createdAt string (date-time)
mustChangePassword boolean
hasRecoveryKey boolean
401 Incorrect username or password (UNAUTHENTICATED). Error
error object
code string
message string
403 Account deactivated (FORBIDDEN). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/login" \
-H "Content-Type: application/json" \
-d '{
"username": "string",
"password": "string"
}'
POST
/v1/apps/{appId}/messenger/cognito-session
No authentication
Exchange a Cognito id token for a NoPeek session
The primary login when Cognito is the IdP. The app authenticates the user against Cognito (password/MFA/refresh), then posts the verified idToken here; NoPeek verifies it, provisions/links the user (by cognito_sub, else by email), and mints a session. Requires Cognito to be configured for this deployment.
Parameters
Request body
Responses
200 Session + profile + workspaces. Session
sessionToken string
Short-lived client credential (nps_…). Hand to the SDK; TTL follows the app’s sessionTtlSeconds.
expiresAt string (date-time)
user MessengerUser
End-user profile for the white-label messenger. Username is typically the email (admins add users by email); the display handle is separate.
userId string
username string
email string | null
nickname string | null
avatarUrl string | null
statusText string | null
mustChangePassword boolean
True until the user completes their forced first-login password change.
hasRecoveryKey boolean
Whether a recovery-key hint has been stored for this account.
lastSeenAt string (date-time) | null
workspaces array of Workspace
workspaceId string
appId string
name string
slug string | null
branding object | null
myRole string | null
Present only when fetched with a session token: the caller’s role. One of: member, admin, owner.
createdAt string (date-time)
mustChangePassword boolean
hasRecoveryKey boolean
401 Invalid or expired sign-in token (UNAUTHENTICATED). Error
error object
code string
message string
501 Cognito is not configured (NOT_IMPLEMENTED). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/cognito-session" \
-H "Content-Type: application/json" \
-d '{
"idToken": "string"
}'
POST
/v1/apps/{appId}/messenger/join
No authentication
Create an account via an org/referral code
The only self-serve way to get an account — there is no open registration. A valid workspace join or referral code is required (referral codes work only when the workspace allows referral join). Creates the user, adds them to the workspace as a member, and returns a session.
Parameters
Request body
code string required
Workspace join or referral code.
username string required
Max length 64.
password string required
email string (email) | null
displayName string | null
Display handle; defaults to the username.
Responses
201 Account created; session issued. Session
sessionToken string
Short-lived client credential (nps_…). Hand to the SDK; TTL follows the app’s sessionTtlSeconds.
expiresAt string (date-time)
user MessengerUser
End-user profile for the white-label messenger. Username is typically the email (admins add users by email); the display handle is separate.
userId string
username string
email string | null
nickname string | null
avatarUrl string | null
statusText string | null
mustChangePassword boolean
True until the user completes their forced first-login password change.
hasRecoveryKey boolean
Whether a recovery-key hint has been stored for this account.
lastSeenAt string (date-time) | null
workspaces array of Workspace
workspaceId string
appId string
name string
slug string | null
branding object | null
myRole string | null
Present only when fetched with a session token: the caller’s role. One of: member, admin, owner.
createdAt string (date-time)
mustChangePassword boolean
hasRecoveryKey boolean
403 Workspace is not accepting referrals (FORBIDDEN). Error
error object
code string
message string
404 Invalid organization code (NOT_FOUND). Error
error object
code string
message string
409 Username taken (ALREADY_EXISTS). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/join" \
-H "Content-Type: application/json" \
-d '{
"code": "string",
"username": "string",
"password": "string"
}'
POST
/v1/apps/{appId}/messenger/join-workspace
Session token
Join an additional workspace (client)
For an already-signed-in user: redeem another workspace’s code to become a multi-org member. Its channels then appear in the unified inbox.
Parameters
Request body
Responses
201 Joined; the workspace (with your role). Workspace
workspaceId string
appId string
name string
slug string | null
branding object | null
myRole string | null
Present only when fetched with a session token: the caller’s role. One of: member, admin, owner.
createdAt string (date-time)
404 Invalid organization code (NOT_FOUND). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/join-workspace" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"code": "string"
}'
GET
/v1/apps/{appId}/messenger/inbox
Session token
Unified multi-org inbox (client)
Every channel the caller is a member of across all their workspaces, newest-activity first, each tagged with its workspace. Includes the caller’s unread count and role per channel.
Parameters
Responses
200 Inbox + the workspaces it spans.
inbox array of InboxItem
channelId string
appId string
channelTypeKey string
kind string
One of: direct, group, broadcast.
e2ee boolean
name string
coverUrl string | null
metadata object
isFrozen boolean
mlsGroupId string | null
memberCount integer
lastMessageAt string (date-time) | null
createdBy string | null
createdAt string (date-time)
workspaceId string | null
workspaceName string | null
communityId string | null
unreadCount integer
myRole string
One of: member, operator.
workspaces array of object
workspaceId string
name string
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/inbox" \
-H "Authorization: Bearer nps_SESSION_TOKEN"
GET
/v1/apps/{appId}/messenger/me
Session token
Get my profile (client)
Parameters
Responses
200 Profile. MessengerUser
userId string
username string
email string | null
nickname string | null
avatarUrl string | null
statusText string | null
mustChangePassword boolean
True until the user completes their forced first-login password change.
hasRecoveryKey boolean
Whether a recovery-key hint has been stored for this account.
lastSeenAt string (date-time) | null
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/me" \
-H "Authorization: Bearer nps_SESSION_TOKEN"
PATCH
/v1/apps/{appId}/messenger/me
Session token
Update my profile (client)
Parameters
Request body (optional)
nickname string
avatarUrl string | null
statusText string | null
Responses
200 Updated profile. MessengerUser
userId string
username string
email string | null
nickname string | null
avatarUrl string | null
statusText string | null
mustChangePassword boolean
True until the user completes their forced first-login password change.
hasRecoveryKey boolean
Whether a recovery-key hint has been stored for this account.
lastSeenAt string (date-time) | null
cURL
curl -X PATCH "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/me" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nickname": "string"
}'
POST
/v1/apps/{appId}/messenger/change-password
Session token
Change my password (client)
Lossless: because the user proves the current password, the client can re-wrap the E2EE backup data-key under the new password (it re-runs backup afterwards). Clears the mustChangePassword flag.
Parameters
Request body
Responses
401 Current password incorrect (UNAUTHENTICATED). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/change-password" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"currentPassword": "string",
"newPassword": "string"
}'
POST
/v1/apps/{appId}/messenger/email-recovery-key
Session token
Save a recovery-key hint (+ optionally email the key) (client)
The recovery code is generated on-device (it wraps the E2EE data key); this stores a last-4 hint and, if the workspace opted in (email_recovery_key) and the account has an email, emails the full code. Emailing a key that can open backups is a deliberate escrow trade-off — see the Backup & recovery guide.
Parameters
Request body
Responses
200 Hint stored.
ok boolean
emailed boolean
hint string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/email-recovery-key" \
-H "Authorization: Bearer nps_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"recoveryCode": "string"
}'
POST
/v1/apps/{appId}/messenger/forgot-password
No authentication
Request a password-reset code
Emails a 6-char, 30-minute reset code to the account (matched by username or email). Always returns ok — it never reveals whether an account exists.
Parameters
Request body
username string required
Username or email.
Responses
200 Accepted (email sent if the account exists).
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/forgot-password" \
-H "Content-Type: application/json" \
-d '{
"username": "string"
}'
POST
/v1/apps/{appId}/messenger/reset-password
No authentication
Reset password with a code
Submits the emailed code + a new password. A forgotten password cannot re-wrap the old E2EE data-key, so if recoveryRequired is true the client must supply the saved recovery key (or rely on org-escrow) to keep old messages — otherwise history starts fresh.
Parameters
Request body
Responses
401 Invalid or expired reset code (UNAUTHENTICATED). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/reset-password" \
-H "Content-Type: application/json" \
-d '{
"username": "string",
"code": "string",
"newPassword": "string"
}'
POST
/v1/apps/{appId}/messenger/users
App server key
Admin-create a messenger user
Server-key only (no public registration). Username defaults to the email. If no password is given a temporary one is generated and returned once; the user is flagged mustChangePassword. Optionally adds the user to a workspace and mirrors the account into Cognito when configured.
Parameters
Request body
username string required
Usually the email. Max length 64.
password string | null
Omit to auto-generate a temporary password.
email string (email) | null
displayName string | null
workspaceId string | null
Add the new user to this workspace.
role string
One of: member, admin, owner. Default "member".
Responses
201 User created; initial password shown once.
409 Username taken (ALREADY_EXISTS). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/users" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"username": "string",
"role": "member"
}'
POST
/v1/apps/{appId}/messenger/users/{userId}/reset-password
App server key
Admin-reset a user’s password
Server-key only. Generates a new temporary password (returned once) and re-flags mustChangePassword. Mirrors to Cognito when configured. The user needs their saved recovery key (or org-escrow) to keep old messages afterwards.
Parameters
Responses
200 Reset; temporary password shown once.
userId string
temporaryPassword string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/messenger/users/user_8m2n1xq4/reset-password" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
Workspaces
Organizations end users join, their communities, membership and roles. Join & referral codes gate access.
GET
/v1/apps/{appId}/workspaces
App server key
List workspaces (mine, or all with a server key)
With a session token: the workspaces the caller belongs to (each with myRole). With a server/org-admin key: every workspace in the app, including codes.
Parameters
Responses
200 Workspaces.
workspaces array of Workspace
workspaceId string
appId string
name string
slug string | null
branding object | null
myRole string | null
Present only when fetched with a session token: the caller’s role. One of: member, admin, owner.
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/workspaces" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/workspaces
App server key
Create a workspace
Server/org-admin key. Auto-generates a join code and a referral code (returned in the admin view).
Parameters
Request body
Responses
201 Workspace (with codes). WorkspaceAdmin
workspaceId string
appId string
name string
slug string | null
branding object | null
myRole string | null
Present only when fetched with a session token: the caller’s role. One of: member, admin, owner.
createdAt string (date-time)
joinCode string
Short code that admits new users as workspace members.
referralCode string
Member-shareable code; honored only when allowReferralJoin is true.
allowReferralJoin boolean
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/workspaces" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "string",
"allowReferralJoin": true
}'
GET
/v1/apps/{appId}/workspaces/{workspaceId}
App server key
Get a workspace
Members see the basic view (with myRole); server/org-admin keys see the admin view with codes.
Parameters
Responses
200 Workspace. Workspace
workspaceId string
appId string
name string
slug string | null
branding object | null
myRole string | null
Present only when fetched with a session token: the caller’s role. One of: member, admin, owner.
createdAt string (date-time)
403 Not a member (FORBIDDEN). Error
error object
code string
message string
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/workspaces/ws_6b3n8kq2" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/workspaces/{workspaceId}/rotate-codes
App server key
Rotate join & referral codes
Workspace admin/owner (or server key). Invalidates the old codes and returns fresh ones.
Parameters
Responses
200 Workspace with new codes. WorkspaceAdmin
workspaceId string
appId string
name string
slug string | null
branding object | null
myRole string | null
Present only when fetched with a session token: the caller’s role. One of: member, admin, owner.
createdAt string (date-time)
joinCode string
Short code that admits new users as workspace members.
referralCode string
Member-shareable code; honored only when allowReferralJoin is true.
allowReferralJoin boolean
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/workspaces/ws_6b3n8kq2/rotate-codes" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
GET
/v1/apps/{appId}/workspaces/{workspaceId}/members
App server key
List workspace members
Parameters
Responses
200 Members with their workspace role.
members array of object
userId string
username string
email string | null
nickname string | null
avatarUrl string | null
statusText string | null
mustChangePassword boolean
True until the user completes their forced first-login password change.
hasRecoveryKey boolean
Whether a recovery-key hint has been stored for this account.
lastSeenAt string (date-time) | null
role string
One of: member, admin, owner.
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/workspaces/ws_6b3n8kq2/members" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
POST
/v1/apps/{appId}/workspaces/{workspaceId}/members
App server key
Add a member
Workspace admin/owner (or server key). The user must already exist in the app.
Parameters
Request body
Responses
404 User not found in this app (NOT_FOUND). Error
error object
code string
message string
cURL
curl -X POST "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/workspaces/ws_6b3n8kq2/members" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"userId": "user_8m2n1xq4",
"role": "member"
}'
PATCH
/v1/apps/{appId}/workspaces/{workspaceId}/members/{userId}
App server key
Set a member’s role
Workspace admin/owner (or server key).
Parameters
Request body
Responses
cURL
curl -X PATCH "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/workspaces/ws_6b3n8kq2/members/user_8m2n1xq4" \
-H "Authorization: Bearer npk_live_SERVER_KEY" \
-H "Content-Type: application/json" \
-d '{
"role": "member"
}'
DELETE
/v1/apps/{appId}/workspaces/{workspaceId}/members/{userId}
App server key
Remove a member
Workspace admin/owner (or server key).
Parameters
Responses
cURL
curl -X DELETE "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/workspaces/ws_6b3n8kq2/members/user_8m2n1xq4" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
GET
/v1/apps/{appId}/workspaces/{workspaceId}/communities
App server key
List communities
Parameters
Responses
200 Communities.
communities array of Community
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/workspaces/ws_6b3n8kq2/communities" \
-H "Authorization: Bearer npk_live_SERVER_KEY"
App configuration
Public force-update policy, session auto-logoff TTL, and the superadmin global policy.
GET
/v1/app-config
No authentication
Get force-update policy + session settings
Public. Safe to poll every ~30s. With ?appId= the per-app policy overlay is merged over the global policy and the app’s session TTL is included. serverTime lets clients detect drift for session expiry.
Parameters
Responses
200 Effective policy, session settings, and server time.
cURL
curl -X GET "https://api.nopeekapi.com/v1/app-config"
PATCH
/v1/admin/app-config
Org admin key
Set the global force-update policy
Superadmin. Merges the given fields into the NoPeek-wide policy used by marketing/admin sites and as the base for per-app overlays.
Request body (optional) AppPolicy
forceRefreshEnabled boolean
refreshToken string
Opaque token; when it changes, running clients reload.
minWebBuild string | null
minIosVersion string | null
minAndroidVersion string | null
title string
message string
iosUpdateUrl string | null
androidUpdateUrl string | null
Responses
200 Updated policy.
policy AppPolicy
Capacitor remote-load force-update policy. Clients poll GET /v1/app-config every ~30s; a change in refreshToken (or a min-version gate the running build fails) triggers the update prompt.
forceRefreshEnabled boolean
refreshToken string
Opaque token; when it changes, running clients reload.
minWebBuild string | null
minIosVersion string | null
minAndroidVersion string | null
title string
message string
iosUpdateUrl string | null
androidUpdateUrl string | null
cURL
curl -X PATCH "https://api.nopeekapi.com/v1/admin/app-config" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"forceRefreshEnabled": true,
"refreshToken": "string",
"title": "string",
"message": "string"
}'
POST
/v1/admin/app-config/force-refresh
Org admin key
Fire a global force-refresh
Superadmin. Bumps refreshToken so every running client reloads to the latest build. Optional title/message customize the prompt.
Request body (optional)
title string
message string
Responses
200 Updated policy.
policy AppPolicy
Capacitor remote-load force-update policy. Clients poll GET /v1/app-config every ~30s; a change in refreshToken (or a min-version gate the running build fails) triggers the update prompt.
forceRefreshEnabled boolean
refreshToken string
Opaque token; when it changes, running clients reload.
minWebBuild string | null
minIosVersion string | null
minAndroidVersion string | null
title string
message string
iosUpdateUrl string | null
androidUpdateUrl string | null
cURL
curl -X POST "https://api.nopeekapi.com/v1/admin/app-config/force-refresh" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY" \
-H "Content-Type: application/json" \
-d '{
"title": "string",
"message": "string"
}'
Admin
Org-scoped and superadmin operations: tamper-evident audit trails, chain verification, usage reports and platform metrics.
GET
/v1/apps/{appId}/audit-logs
Org admin key
Org-scoped audit trail
The tamper-evident audit trail for one app — an org admin (or superadmin) sees only their own app’s events. Newest first. Metadata only; message bodies are E2EE and never logged.
Parameters
Responses
200 Audit entries.
entries array of AuditEntry
auditId string
seq integer
Monotonic sequence used for ordering and the before= cursor.
orgId string | null
appId string | null
workspaceId string | null
actorType string
e.g. user, app_server, org_admin, system.
actorId string | null
action string
e.g. auth.login.success, admin.user.create, auth.password.reset_failed.
targetType string | null
targetId string | null
outcome string
One of: success, failure, denied.
ip string | null
userAgent string | null
metadata object
hash string
SHA-256 hex of this row’s link in the chain.
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/apps/app_7f3kq2c9/audit-logs" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY"
GET
/v1/admin/audit
Org admin key
Platform-wide audit trail (superadmin)
Every org’s audit events, filterable. Superadmin only.
Parameters
Responses
200 Audit entries.
entries array of AuditEntry
auditId string
seq integer
Monotonic sequence used for ordering and the before= cursor.
orgId string | null
appId string | null
workspaceId string | null
actorType string
e.g. user, app_server, org_admin, system.
actorId string | null
action string
e.g. auth.login.success, admin.user.create, auth.password.reset_failed.
targetType string | null
targetId string | null
outcome string
One of: success, failure, denied.
ip string | null
userAgent string | null
metadata object
hash string
SHA-256 hex of this row’s link in the chain.
createdAt string (date-time)
cURL
curl -X GET "https://api.nopeekapi.com/v1/admin/audit" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY"
GET
/v1/admin/audit/verify
Org admin key
Verify the audit hash chain (superadmin)
Recomputes the hash chain (optionally for one app) and reports whether it is intact. If broken, returns the audit id of the first bad link.
Parameters
Responses
cURL
curl -X GET "https://api.nopeekapi.com/v1/admin/audit/verify" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY"
GET
/v1/orgs/usage
Org admin key
My org’s usage vs plan limits
Org-admin key. Current apps/users/MAU/messages/workspaces against plan ceilings, with proximity and warnings.
Responses
200 Usage report. UsageReport
plan string
One of: free, starter, pro, enterprise.
subscriptionStatus string | null
usage object
limits object
Plan ceilings. Enterprise is effectively unlimited.
apps integer
users integer
mau integer
messagesPerMonth integer
workspaces integer
proximity object
Per-metric detail keyed by usage metric.
warnings array of object
metric string
status string
One of: warning, over.
used integer
limit integer
cURL
curl -X GET "https://api.nopeekapi.com/v1/orgs/usage" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY"
GET
/v1/admin/orgs/{orgId}/usage
Org admin key
Any org’s usage (superadmin)
Parameters
Responses
200 Usage report. UsageReport
plan string
One of: free, starter, pro, enterprise.
subscriptionStatus string | null
usage object
limits object
Plan ceilings. Enterprise is effectively unlimited.
apps integer
users integer
mau integer
messagesPerMonth integer
workspaces integer
proximity object
Per-metric detail keyed by usage metric.
warnings array of object
metric string
status string
One of: warning, over.
used integer
limit integer
cURL
curl -X GET "https://api.nopeekapi.com/v1/admin/orgs/org_2p7m4xd8/usage" \
-H "Authorization: Bearer npk_live_ORG_ADMIN_KEY"