Saleo Public API
Last updated: October 21, 2026
Demo Personalization workflow
Discover Saleo Live demos, retrieve personalization tokens, and create or maintain personalized views of them programmatically.
Field | Value |
API version | v1 |
Request URL | https://api.platform.saleo.io/ |
Protocol | RPC over JSON; POST-only |
Authentication | Bearer token — a per-user Saleo API key |
1. Overview
The Saleo Public API lets an authorized integration discover Saleo Live demo templates, inspect their text personalization tokens, create personalized views with token overrides, update those views later, share them with colleagues, and hand them over to the colleague they are for — all outside the Saleo Portal.
Support Operations
Endpoint | Description |
| Search demos and personalized views visible to the key's user. |
| List the text tokens of a template or personalized view. |
| Create a personalized view of a template. |
| Update token values, rename, share, and/or transfer ownership of a view. |
| Resolve a colleague’s email to the user id needed to share or transfer a view. |
2. Authentication
All requests require a bearer token:
Authorization: Bearer <user_api_key>Unlike team-wide tokens, this API uses user API keys. A user API key is bound to the Saleo user who created it: every call acts as that user, sees only the demos they can see, and creates views owned by them.
Creating and managing your key
Any user can create their own key in the Saleo Portal: profile menu → API Keys → Create My API Key. The key value is shown only once, at creation — copy it immediately; it cannot be retrieved later.
Each user can hold one active key at a time. To rotate, revoke the existing key and create a new one.
Revoking a key is permanent and takes effect on its next request. Keys can also be renamed to record what they're used for.
Admins can see and revoke every key on the team under Settings → Manage SCIM/API Access Tokens, including when each was last used. Non-admin users only see their own key.
A key stops working the moment its user is deactivated.
Partner teams: user API keys are not available to Saleo partner (reseller) accounts — neither to create nor to use. This does not apply to standard customer tenants.
Security Recommendation
Call this API from your own backend or approved integration layer, not directly from an untrusted browser client. Store the API key the same way you'd store any credential — encrypted at rest, never embedded in front-end code — and rotate it if it may have been exposed.
3. Quick start: create a personalized demo view
Step 1 — Find a personalizable demo
curl --request POST \
--url https://api.platform.saleo.io/v1/get_live_demos \
--header "Authorization: Bearer $SALEO_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"search": "EA-4711",
"personalizableOnly": true,
"limit": 25
}'
{
"data": [
{
"id": "d3f2a1b0-...",
"name": "EA-4711 ACME Overview",
"tags": ["Retail"],
"owner": { "userId": "u-1", "name": "Sally Seller", "email": "rep@acme.com" },
"personalizable": true,
"personalizedView": false,
"playUrl": "https://.../play/d3f2a1b0-...",
"createdAt": "2026-07-07T12:00:00Z",
"updatedAt": "2026-07-07T12:00:00Z"
}
]
}Step 2 — List the template's tokens
curl --request POST \
--url https://api.platform.saleo.io/v1/get_demo_tokens \
--header "Authorization: Bearer $SALEO_API_KEY" \
--header "Content-Type: application/json" \
--data '{ "demoId": "d3f2a1b0-..." }'
{
"data": [
{ "id": "t-company", "name": "%%company%%", "description": "Customer name",
"tags": ["Company Info"], "value": "ACME", "overridden": false }
]
}Step 3 — Create a personalized view
curl --request POST \
--url https://api.platform.saleo.io/v1/create_personalized_view \
--header "Authorization: Bearer $SALEO_API_KEY" \
--header "Content-Type: application/json" \
--data '{
"demoId": "d3f2a1b0-...",
"name": "ACME for Globex",
"tokenValues": [ { "tokenId": "t-company", "value": "Globex" } ]
}'
{
"view": { "id": "v-0000-...", "name": "ACME for Globex",
"personalizedView": true, "parentDemoId": "d3f2a1b0-...",
"playUrl": "https://.../play/v-0000-..." },
"appliedTokens": [
{ "id": "t-company", "name": "%%company%%", "value": "Globex", "overridden": true }
]
}Every token value in the request must apply. An unknown token id or an over-length value (1000 char max) fails the whole call and creates nothing — there's no partial success to check for.
Step 4 — Update it later
Use the same view id to refresh token values, rename the view, share it with colleagues, or hand it over to the rep it was personalized for as the deal changes — see update_personalized_view below.
4. Endpoint reference
POST /v1/get_live_demos
Returns the demos the key’s user works with: personalizable templates they can view, plus personalized views they own or that are shared with them. Returned newest-created first.
Field | Type | Required | Description |
search | string | No | Case-insensitive match over demo names and tag labels. |
tags | array | No | Tag labels; a demo matches if it carries any of them. |
ownerEmail | string | No | Demos owned by this user, matched case-insensitively. |
sharedWithEmail | string | No | Demos shared directly with this user (view or edit), matched case-insensitively. |
personalizableOnly | boolean | No | Governs templates only, default true. Personalized views are always returned — filter on personalizedView client-side. |
limit | integer | No | Default 100, capped at 500. |
continuationToken | string | No | Cursor from the previous response. |
Filters combine: a demo has to match every filter you send. sharedWithEmail matches direct user shares only. Shares made in the portal to a user group, a role or the whole team do not match. Both email filters only narrow what the key’s user can already see. To list a rep’s views by email, the key’s user must still be able to reach them, for example as their owner or a user they are shared with.
owner in the response is the demo’s current owner. After a transfer, this is the new owner, not the user who created the view.
POST /v1/get_demo_tokens
Returns the text tokens of a template or a personalized view — tokens declared at any level of the demo's parent chain are included. Image tokens are not returned; this API personalizes text only.
Field | Type | Required | Description |
demoId | string | Yes | A template id or a personalized view id. |
POST /v1/create_personalized_view
Creates a personalized view of a template: a private copy owned by the key's user, carrying the requested token values. Only its owner can see the view until it is shared, through update_personalized_view or in the portal.
Field | Type | Required | Description |
demoId | string | Yes | The template to personalize; must be personalizable. |
name | string | Yes | Must be unique within the team. |
tokenValues | array | No | Objects of { |
POST /v1/update_personalized_view
Changes token values on an existing view. It can also rename the view, share it with colleagues and transfer its ownership. An integration can keep one view id and refresh it as the deal changes.
These users can update a view, matching the Saleo Portal:
the view’s owner
a team admin
a user the view is shared with as an editor
Field | Type | Required | Description |
viewId | string | Yes | From |
tokenValues | array | No | Only the tokens named here change; the rest keep their values. |
sharedWith | array | No | Objects of |
newOwnerUserId | string | No | From |
name | string | No | Rename the view; unique within the team. Re-sending the current name does nothing. |
Sharing rules
Add-only. A user named in
sharedWithis added to whoever the view is already shared with. An existing share can be raised from view to edit, but never lowered or removed — there's no un-share through this API. Remove a share in the portal.canEditin the portal depends on the recipient’s seat. In the Saleo Portal,canEditonly lets users edit if their seat allows editing at all. A sales-seat colleague keeps view access there, whatever you send. Through this API, a user the view is shared with directly as an editor can update it on any seat.
The owner is ignored. A share naming the view's own owner is dropped silently, since they already have access.
All or nothing. If any
userIdis not an active user of the key's team, the whole call fails and nobody is shared.
Sharing a view also makes its parent template visible to that user in the portal, so they can open the view without an extra step.
Transferring ownership
Send newOwnerUserId to hand the view to another user, for example the rep it was personalized for. The rep can then find it with ownerEmail, or open it in the portal as their own.
Anyone who can update the view can transfer it: its owner, a team admin or an editor.
The previous owner keeps edit access. They are added as an editor, so they can keep updating the view through this API on any seat.
The new owner’s existing share is removed, since owning the view already gives them access. A
sharedWithentry in the same call naming the new owner is dropped the same way.Naming the current owner does nothing.
The new owner has to be an active user of the key’s team. Otherwise the whole call fails and nothing changes.
A transfer can be combined with token, name and sharing changes in a single call.
After a transfer, sharedWith in the response includes the previous owner as an editor.
POST /v1/get_user_id
Resolves a colleague's email address to the user id that the sharedWith and newOwnerUserId fields take. Only active users of the key's own team resolve.
Field | Type | Required | Description |
string | Yes | Matched case-insensitively; surrounding spaces ignored. |
A user of another team, a deactivated user, and one who hasn't yet accepted their invitation all return the same 404 — the API never reveals who exists elsewhere in Saleo.
5. Error format
All error responses share this shape. The HTTP status carries the error class; the body's code field is the corresponding gRPC status number.
{
"code": 5,
"error": "Personalized view not found"
}HTTP status | Meaning |
400 | A required field is missing, a token id is unknown, or a value exceeds 1000 characters — nothing changes. Also covers demo/view-type mismatches, e.g. “This demo does not allow personalized views.” Also fails when a share entry carries no userId ("sharedWith entries require a userId" — resolve one via get_user_id), when a shared userId isn't an active user of the team, or when more than 100 users are named in one call ("Exceeded limit of 100 users to share with" — split across calls; sharing is add-only, so earlier shares are kept). Also fails when |
401 | Missing API key, an invalid access token, a team-wide token used in place of a user API key, a deactivated user, or a deactivated company account. |
403 | The team is a partner team. On update: “You do not have edit access to this personalized view” — the key’s user isn’t the view’s owner, a team admin, or an editor of the view. |
404 | The demo or view does not exist, or belongs to another team. |
404 — "User not found." | No active user of your team holds that email. |
409 | A demo with the requested name already exists. |
6. Integration recommendations
Run Saleo API calls from your own backend service or approved integration layer, not directly from an untrusted browser client.
Map your own business identifiers (transaction, conversation, opportunity, or workflow id) to Saleo demoId and viewId values in a durable integration table.
Cache demo discovery results briefly, but re-read tokens before personalizing when template authors may have changed them.
Validate that a selected demo is personalizable before creating a view.
Treat playUrl as the base launch URL returned by Saleo — append your own ?startUrl= parameter before sharing it, rather than reconstructing the rest of the URL yourself.
Since create and update calls fail atomically, validate token ids and values client-side before sending to avoid unnecessary round trips.