Search docs ⌘K
esc
Type to search across all documentation pages

Platform

Manage organizations, projects, and tokens. All Platform endpoints require a session token from /api/auth/sign-in/email.

Organizations

Organizations are the top-level container for projects, billing, and team members. Every user belongs to at least one org. All Platform endpoints require Authorization: Bearer <session_token> obtained from /api/auth/sign-in/email.

List Organizations

GET
/core/organizations

List organizations the current user is a member of. Required scope: orgs:read (for OAuth tokens); portal tokens are trusted implicitly.

curl
curl https://api.saasignal.saastemly.com/core/organizations \
  -H "Authorization: Bearer <session_token>"
json — 200 OK
{ "status": "ok" }
typescript
const orgs = await ss.core.orgs.list()
  1. Open Dashboard
  2. Your organizations are listed in the sidebar
Error responses
401 Unauthorized

Create Organization

POST
/core/organizations

Create a new organization and add the authenticated user as the owner. Required scope: orgs:write (for OAuth tokens); portal tokens are trusted implicitly.

curl
curl -X POST https://api.saasignal.saastemly.com/core/organizations \
  -H "Authorization: Bearer <session_token>" \
  -H "Content-Type: application/json" \
  -d '{"name":"example"}'
json — 201 Created
{ "status": "ok" }
typescript
const org = await ss.core.orgs.create({ name: 'Acme Corp' })
  1. Open Dashboard
  2. Click Create Organization
  3. Enter a name and click Create
Body field
Type
Description
name required
string
Organization name (min 1 char)
min 1 chars
slug
string
URL-friendly slug. Lowercase alphanumeric and hyphens only (/^[a-z0-9-]+$/). Auto-generated if omitted.
min 1 charspattern ^[a-z0-9-]+$
Error responses
401 Unauthorized
409 Slug already taken

Get Organization

GET
/core/organizations/{org_id}
orgs:read

Retrieve a single organization by ID.

curl
curl https://api.saasignal.saastemly.com/core/organizations/{org_id} \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
const org = await ss.core.orgs.get('org_01JNXS...')
  1. Open Dashboard
  2. Click on the organization name in the sidebar to view its details
Path param
Type
Description
org_id required
string
Organization ID
Error responses
401 Unauthorized
404 Not found

Update Organization

PATCH
/core/organizations/{org_id}
orgs:write

Update an organization's name and/or slug.

curl
curl -X PATCH https://api.saasignal.saastemly.com/core/organizations/{org_id} \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{}'
json — 200 OK
{ "status": "ok" }
typescript
await ss.core.orgs.update('org_01JNXS...', { name: 'Acme Inc' })
  1. Open Dashboard → select your organization
  2. Go to Settings and update the organization name
Path param
Type
Description
org_id required
string
Organization ID
Body field
Type
Description
name
string
Updated organization name
slug
string
Updated URL slug (/^[a-z0-9-]+$/)
pattern ^[a-z0-9-]+$
Error responses
401 Unauthorized
404 Not found
409 Conflict

Delete Organization

DELETE
/core/organizations/{org_id}

Permanently delete an organization. Required scope: * (wildcard)

curl
curl -X DELETE https://api.saasignal.saastemly.com/core/organizations/{org_id} \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"confirm_name":"..."}'
typescript
await ss.core.orgs.delete('org_01JNXS...')
  1. Open Dashboard → select your organization
  2. Go to SettingsDanger ZoneDelete Organization
Path param
Type
Description
org_id required
string
Organization ID
Body field
Type
Description
confirm_name required
string
Type the org name to confirm deletion
min 1 chars
Error responses
401 Unauthorized
404 Not found

Members

Manage organization members and their scoped permissions.

List Members

GET
/core/organizations/{org_id}/members
orgs:read

List all members of an organization with their scopes.

curl
curl https://api.saasignal.saastemly.com/core/organizations/{org_id}/members \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
const members = await ss.core.orgs.listMembers('org_01JNXS...')
  1. Open Dashboard → select your organization
  2. Go to Members to see all team members and their roles
Path param
Type
Description
org_id required
string
Organization ID
Error responses
401 Unauthorized
404 Not found

Invite Member

POST
/core/organizations/{org_id}/members/invite
orgs:write

Send an email invitation to join the organization with specified scopes.

curl
curl -X POST https://api.saasignal.saastemly.com/core/organizations/{org_id}/members/invite \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"email":"..."}'
json — 202 Accepted
{ "status": "ok" }
typescript
await ss.core.orgs.invite('org_01JNXS...', {
  email: 'alice@acme.com',
  scopes: ['kv:read', 'kv:write', 'channels:publish'],
})
  1. Open Dashboard → select your organization
  2. Go to Members → click Invite
  3. Enter the email address and select permissions
Path param
Type
Description
org_id required
string
Organization ID
Body field
Type
Description
email required
string
Email address to invite
pattern ^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$
scopes
array
Permission scopes to grant (e.g. ["kv:read", "kv:write"])
project_id
string
Restrict scopes to a specific project
Error responses
401 Unauthorized

Get My Scopes

GET
/core/organizations/{org_id}/my-scopes
orgs:read

Returns the resolved scopes for the authenticated user in this organization.

curl
curl https://api.saasignal.saastemly.com/core/organizations/{org_id}/my-scopes \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
const scopes = await ss.core.orgs.myScopes('org_01JNXS...')
  1. Open Dashboard → select your organization
  2. Go to Members → your scopes are shown next to your name
Path param
Type
Description
org_id required
string
Organization ID
Error responses
401 Unauthorized
403 Forbidden

Update Member Scopes

PUT
/core/organizations/{org_id}/members/{user_id}/scopes
orgs:write

Replace a member's scopes. Cannot modify owner scopes.

curl
curl -X PUT https://api.saasignal.saastemly.com/core/organizations/{org_id}/members/{user_id}/scopes \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"scopes":[]}'
typescript
await ss.core.orgs.updateMemberScopes('org_01JNXS...', 'u_123', {
  scopes: ['kv:read', 'channels:subscribe'],
})
  1. Open Dashboard → select your organization
  2. Go to Members → click on a member
  3. Update their scopes and click Save
Path param
Type
Description
org_id required
string
Organization ID
user_id required
string
User ID of the member
Body field
Type
Description
scopes required
array
New scopes array (replaces existing scopes)
Error responses
401 Unauthorized
403 Forbidden
404 Not found

Remove Member

DELETE
/core/organizations/{org_id}/members/{user_id}
orgs:write

Remove a member from the organization.

curl
curl -X DELETE https://api.saasignal.saastemly.com/core/organizations/{org_id}/members/{user_id} \
  -H "Authorization: Bearer sk_live_..."
typescript
await ss.core.orgs.removeMember('org_01JNXS...', 'u_123')
  1. Open Dashboard → select your organization
  2. Go to Members → click on a member → Remove
Path param
Type
Description
org_id required
string
Organization ID
user_id required
string
User ID to remove
Error responses
401 Unauthorized
403 Forbidden
404 Not found

Projects

Projects live inside organizations and isolate KV namespaces, channels, jobs, and token balances.

List Projects

GET
/core/organizations/{org_id}/projects
projects:read

List all projects belonging to an organization.

curl
curl https://api.saasignal.saastemly.com/core/organizations/{org_id}/projects \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
const projects = await ss.core.orgs.listProjects('org_01JNXS...')
  1. Open Dashboard → select your organization
  2. Go to Projects to see all projects
Path param
Type
Description
org_id required
string
Organization ID
Error responses
401 Unauthorized

Create Project

POST
/core/organizations/{org_id}/projects
projects:write

Create a new project within an organization.

curl
curl -X POST https://api.saasignal.saastemly.com/core/organizations/{org_id}/projects \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"example"}'
json — 201 Created
{ "status": "ok" }
typescript
const project = await ss.core.orgs.createProject('org_01JNXS...', { name: 'Production' })
  1. Open Dashboard → select your organization
  2. Go to Projects → click Create Project
  3. Enter a name and click Create
Path param
Type
Description
org_id required
string
Organization ID
Body field
Type
Description
name required
string
Project name (min 1 char)
min 1 chars
description
string
Project description
region
string
Region: wnam, enam, weur, eeur, apac, oc, afr, me. Default enam.
values: wnam, enam, weur, eeur, apac, oc, afr, medefault enam
Error responses
401 Unauthorized

Get Project

GET
/core/organizations/{org_id}/projects/{project_id}
projects:read

Retrieve a single project by ID.

curl
curl https://api.saasignal.saastemly.com/core/organizations/{org_id}/projects/{project_id} \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
const project = await ss.core.orgs.getProject('org_01JNXS...', 'proj_01JNXS...')
  1. Open Dashboard → select your organization
  2. Go to Projects and click on any project to view its details
Path param
Type
Description
org_id required
string
Organization ID
project_id required
string
Project ID
Error responses
401 Unauthorized
404 Not found

Delete Project

DELETE
/core/organizations/{org_id}/projects/{project_id}
projects:write

Permanently delete a project and all associated data.

curl
curl -X DELETE https://api.saasignal.saastemly.com/core/organizations/{org_id}/projects/{project_id} \
  -H "Authorization: Bearer sk_live_..."
typescript
await ss.core.orgs.deleteProject('org_01JNXS...', 'proj_01JNXS...')
  1. Open Dashboard → select your project
  2. Go to SettingsDanger ZoneDelete Project
Path param
Type
Description
org_id required
string
Organization ID
project_id required
string
Project ID
Error responses
401 Unauthorized
404 Not found

Tokens

Create and manage project API keys with scoped permissions.

List Tokens

GET
/core/organizations/{org_id}/tokens
tokens:read

List all tokens (API keys) for an organization, optionally filtered by project.

curl
curl https://api.saasignal.saastemly.com/core/organizations/{org_id}/tokens \
  -H "Authorization: Bearer sk_live_..."
json — 200 OK
{ "status": "ok" }
typescript
const tokens = await ss.core.orgs.listTokens('org_01JNXS...')
  1. Open Dashboard → select your project
  2. Go to Tokens to see all API keys and their scopes
Path param
Type
Description
org_id required
string
Organization ID
Query param
Type
Description
project_id
string
Filter tokens by project
Error responses
401 Unauthorized
404 Not found

Create Token

POST
/core/organizations/{org_id}/tokens
tokens:write

Create a new scoped API key for an organization or a specific project. The raw sk_live_… secret is returned only once in the response.

curl
curl -X POST https://api.saasignal.saastemly.com/core/organizations/{org_id}/tokens \
  -H "Authorization: Bearer sk_live_..." \
  -H "Content-Type: application/json" \
  -d '{"name":"example","scopes":[]}'
json — 201 Created
{ "status": "ok" }
typescript
const token = await ss.core.orgs.createToken({
  org_id: 'org_01JNXS...',
  project_id: 'proj_01JNXS...',
  name: 'Production Key',
  scopes: ['kv:read', 'kv:write', 'channels:publish'],
})
  1. Open Dashboard → select your project
  2. Go to Tokens → click Create Token
  3. Name your token, select scopes, and click Create
  4. Copy the sk_live_... secret — it's shown only once
Path param
Type
Description
org_id required
string
Organization ID
Body field
Type
Description
name required
string
Token display name (min 1 char)
min 1 chars
scopes required
array
Permission scopes (e.g. ["kv:read", "kv:write"])
project_id
string
Scope token to a specific project
expires_at
string
ISO 8601 expiration datetime
pattern ^(?:(?:\d\d[2468][048]|\d\d[13579][26]|\d\d0[48]|[02468][048]00|[13579][26]00)-02-29|\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\d|30)|(?:02)-(?:0[1-9]|1\d|2[0-8])))T(?:(?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d+)?)?(?:Z))$
Error responses
401 Unauthorized
403 Forbidden
404 Not found

Revoke Token

DELETE
/core/organizations/{org_id}/tokens/{token_id}
tokens:write

Permanently revoke a token. Revoked tokens cannot be used for authentication.

curl
curl -X DELETE https://api.saasignal.saastemly.com/core/organizations/{org_id}/tokens/{token_id} \
  -H "Authorization: Bearer sk_live_..."
typescript
await ss.core.orgs.revokeToken('org_01JNXS...', 'tok_01JNXS...')
  1. Open Dashboard → select your project
  2. Go to Tokens → click on a token → Revoke
Path param
Type
Description
org_id required
string
Organization ID
token_id required
string
Token ID to revoke
Error responses
401 Unauthorized
404 Not found

Create Browser Token

POST
/core/tokens/browser
inherits from the parent credential.

Create a short-lived browser token (bt_*) by exchanging an API key (sk_live_*) or OAuth JWT. The browser token is a stateless JWT that can be used directly from browser JavaScript. Requested scopes are intersected with the parent key's scopes. Required auth: API key (sk_live_*) or OAuth JWT

curl
curl -X POST https://api.saasignal.saastemly.com/core/tokens/browser \
  -H "Authorization: Bearer sk_live_abc123..." \
  -H "Content-Type: application/json" \
  -d '{ "scopes": ["channels:subscribe", "kv:read"], "ttl": 900 }'
json — 200 OK
{
  "token": "bt_eyJhbGciOi...",
  "expires_at": "2026-03-10T13:15:00Z",
  "ttl": 900,
  "scopes": ["channels:subscribe", "kv:read"],
  "project_id": "proj_01JNXS...",
  "org_id": "org_01JNXS..."
}
typescript
const { token, expires_at } = await SaaSignalClient.createBrowserToken({
  apiKey: 'sk_live_abc123...',
  scopes: ['channels:subscribe', 'kv:read'],
  ttl: 900,
})
// token is a short-lived bt_* JWT safe for browser use
  1. Browser tokens are created programmatically via the API or SDK — exchange a server-side sk_live_* key for a short-lived bt_* token safe for browser use
  2. Call SaaSignalClient.createBrowserToken() from your server, then pass the token to your frontend
Body field
Type
Description
scopes
array
Permission scopes for the browser token (must be a subset of the parent API key's scopes). If omitted, inherits all parent key scopes.
ttl
integer
Time-to-live in seconds (default 900, min 60, max 1800)
Error responses
400 Bad request
401 Unauthorized
403 Forbidden