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
List organizations the current user is a member of. Required scope: orgs:read (for OAuth tokens); portal tokens are trusted implicitly.
curl https://api.saasignal.saastemly.com/core/organizations \
-H "Authorization: Bearer <session_token>"
{ "status": "ok" }
const orgs = await ss.core.orgs.list()
- Open Dashboard
- Your organizations are listed in the sidebar
Create Organization
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 -X POST https://api.saasignal.saastemly.com/core/organizations \
-H "Authorization: Bearer <session_token>" \
-H "Content-Type: application/json" \
-d '{"name":"example"}'
{ "status": "ok" }
const org = await ss.core.orgs.create({ name: 'Acme Corp' })
- Open Dashboard
- Click Create Organization
- Enter a name and click Create
name requiredslug/^[a-z0-9-]+$/). Auto-generated if omitted.^[a-z0-9-]+$Get Organization
Retrieve a single organization by ID.
curl https://api.saasignal.saastemly.com/core/organizations/{org_id} \
-H "Authorization: Bearer sk_live_..."
{ "status": "ok" }
const org = await ss.core.orgs.get('org_01JNXS...')
- Open Dashboard
- Click on the organization name in the sidebar to view its details
org_id requiredUpdate Organization
Update an organization's name and/or slug.
curl -X PATCH https://api.saasignal.saastemly.com/core/organizations/{org_id} \
-H "Authorization: Bearer sk_live_..." \
-H "Content-Type: application/json" \
-d '{}'
{ "status": "ok" }
await ss.core.orgs.update('org_01JNXS...', { name: 'Acme Inc' })
- Open Dashboard → select your organization
- Go to Settings and update the organization name
org_id requirednameslug/^[a-z0-9-]+$/)^[a-z0-9-]+$Delete Organization
Permanently delete an organization. Required scope: * (wildcard)
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":"..."}'
await ss.core.orgs.delete('org_01JNXS...')
- Open Dashboard → select your organization
- Go to Settings → Danger Zone → Delete Organization
org_id requiredconfirm_name requiredMembers
Manage organization members and their scoped permissions.
List Members
List all members of an organization with their scopes.
curl https://api.saasignal.saastemly.com/core/organizations/{org_id}/members \
-H "Authorization: Bearer sk_live_..."
{ "status": "ok" }
const members = await ss.core.orgs.listMembers('org_01JNXS...')
- Open Dashboard → select your organization
- Go to Members to see all team members and their roles
org_id requiredInvite Member
Send an email invitation to join the organization with specified scopes.
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":"..."}'
{ "status": "ok" }
await ss.core.orgs.invite('org_01JNXS...', {
email: 'alice@acme.com',
scopes: ['kv:read', 'kv:write', 'channels:publish'],
})
- Open Dashboard → select your organization
- Go to Members → click Invite
- Enter the email address and select permissions
org_id requiredemail required^(?!\.)(?!.*\.\.)([A-Za-z0-9_'+\-\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\-]*\.)+[A-Za-z]{2,}$scopes["kv:read", "kv:write"])project_idGet My Scopes
Returns the resolved scopes for the authenticated user in this organization.
curl https://api.saasignal.saastemly.com/core/organizations/{org_id}/my-scopes \
-H "Authorization: Bearer sk_live_..."
{ "status": "ok" }
const scopes = await ss.core.orgs.myScopes('org_01JNXS...')
- Open Dashboard → select your organization
- Go to Members → your scopes are shown next to your name
org_id requiredUpdate Member Scopes
Replace a member's scopes. Cannot modify owner scopes.
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":[]}'
await ss.core.orgs.updateMemberScopes('org_01JNXS...', 'u_123', {
scopes: ['kv:read', 'channels:subscribe'],
})
- Open Dashboard → select your organization
- Go to Members → click on a member
- Update their scopes and click Save
org_id requireduser_id requiredscopes requiredRemove Member
Remove a member from the organization.
curl -X DELETE https://api.saasignal.saastemly.com/core/organizations/{org_id}/members/{user_id} \
-H "Authorization: Bearer sk_live_..."
await ss.core.orgs.removeMember('org_01JNXS...', 'u_123')
- Open Dashboard → select your organization
- Go to Members → click on a member → Remove
org_id requireduser_id requiredProjects
Projects live inside organizations and isolate KV namespaces, channels, jobs, and token balances.
List Projects
List all projects belonging to an organization.
curl https://api.saasignal.saastemly.com/core/organizations/{org_id}/projects \
-H "Authorization: Bearer sk_live_..."
{ "status": "ok" }
const projects = await ss.core.orgs.listProjects('org_01JNXS...')
- Open Dashboard → select your organization
- Go to Projects to see all projects
org_id requiredCreate Project
Create a new project within an organization.
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"}'
{ "status": "ok" }
const project = await ss.core.orgs.createProject('org_01JNXS...', { name: 'Production' })
- Open Dashboard → select your organization
- Go to Projects → click Create Project
- Enter a name and click Create
org_id requiredname requireddescriptionregionwnam, enam, weur, eeur, apac, oc, afr, me. Default enam.wnam, enam, weur, eeur, apac, oc, afr, medefault enamGet Project
Retrieve a single project by ID.
curl https://api.saasignal.saastemly.com/core/organizations/{org_id}/projects/{project_id} \
-H "Authorization: Bearer sk_live_..."
{ "status": "ok" }
const project = await ss.core.orgs.getProject('org_01JNXS...', 'proj_01JNXS...')
- Open Dashboard → select your organization
- Go to Projects and click on any project to view its details
org_id requiredproject_id requiredDelete Project
Permanently delete a project and all associated data.
curl -X DELETE https://api.saasignal.saastemly.com/core/organizations/{org_id}/projects/{project_id} \
-H "Authorization: Bearer sk_live_..."
await ss.core.orgs.deleteProject('org_01JNXS...', 'proj_01JNXS...')
- Open Dashboard → select your project
- Go to Settings → Danger Zone → Delete Project
org_id requiredproject_id requiredTokens
Create and manage project API keys with scoped permissions.
List Tokens
List all tokens (API keys) for an organization, optionally filtered by project.
curl https://api.saasignal.saastemly.com/core/organizations/{org_id}/tokens \
-H "Authorization: Bearer sk_live_..."
{ "status": "ok" }
const tokens = await ss.core.orgs.listTokens('org_01JNXS...')
- Open Dashboard → select your project
- Go to Tokens to see all API keys and their scopes
org_id requiredproject_idCreate Token
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 -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":[]}'
{ "status": "ok" }
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'],
})
- Open Dashboard → select your project
- Go to Tokens → click Create Token
- Name your token, select scopes, and click Create
- Copy the
sk_live_...secret — it's shown only once
org_id requiredname requiredscopes required["kv:read", "kv:write"])project_idexpires_at^(?:(?:\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))$Revoke Token
Permanently revoke a token. Revoked tokens cannot be used for authentication.
curl -X DELETE https://api.saasignal.saastemly.com/core/organizations/{org_id}/tokens/{token_id} \
-H "Authorization: Bearer sk_live_..."
await ss.core.orgs.revokeToken('org_01JNXS...', 'tok_01JNXS...')
- Open Dashboard → select your project
- Go to Tokens → click on a token → Revoke
org_id requiredtoken_id requiredCreate Browser Token
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 -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 }'
{
"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..."
}
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
- Browser tokens are created programmatically via the API or SDK — exchange a server-side
sk_live_*key for a short-livedbt_*token safe for browser use - Call
SaaSignalClient.createBrowserToken()from your server, then pass the token to your frontend
scopesttl