Customer access
Give each customer the correct project or location scope through an embedded Signal application or a short-lived link to hosted Signal.
Choose embedded or hosted access
Both access modes use an embedded identity and its project or location grants. Choose where the customer should work.
viewer or editor.allowed_origins. The same allowlist validates an optional hosted-login return_url.Provision identities and access grants
Use a workspace-scoped key with identities:manage. The external user ID is unique within the workspace, and repeating the request updates the same identity.
# Upsert an identity using your stable user ID.
curl --request POST \
--url 'https://api.signal.ceyo.ai/v1/embedded-identities/customer-user-42' \
--header "Authorization: Bearer ${SIGNAL_API_KEY}" \
--header 'Content-Type: application/json' \
--data '{
"email": "user-42@example.com",
"name": "User 42"
}'
# Grant access to one project.
curl --request POST \
--url 'https://api.signal.ceyo.ai/v1/embedded-identities/customer-user-42/projects/customer-project/access' \
--header "Authorization: Bearer ${SIGNAL_API_KEY}" \
--header 'Content-Type: application/json' \
--data '{ "role": "viewer" }'/projects/{project_id}/locations/{location_id}/access path to restrict access to one location. To grant one to 100 locations in the same project without minting a session, POST /projects/{project_id}/locations/access with location_grants.location_scope: "granted" and no location_id to expose active direct location grants in one project. Signal resolves them live, preserves each location's role, and ignores project grants for this scope. Revoking one grant removes only that location. The embed exposes Overview and Locations, not project-wide routes.viewer, editor, or admin. Embed sessions expose only viewer or editor; an admin grant resolves to editor in the iframe.disabled to block new access and revoke its active embed sessions. Delete a grant to remove that resource scope.POST /embedded-identities/{external_user_id}/projects/{project_id}/locations/access atomically ensures one to 100 direct location grants, including admin, without issuing a session. Use this for onboarding. POST /embed/sessions can still upsert an identity, create viewer or editor grants, and issue the first session through provision.location_grants. Inline provisioning requires a workspace-scoped key with both embed_sessions:create and identities:manage.{
"location_grants": [
{ "location_id": "store-amsterdam", "role": "editor" },
{ "location_id": "store-utrecht", "role": "viewer" }
]
}{
"external_user_id": "regional-manager-42",
"project_id": "customer-project",
"location_scope": "granted",
"provision": {
"location_grants": [
{ "location_id": "store-amsterdam", "role": "editor" },
{ "location_id": "store-utrecht", "role": "viewer" }
]
}
}Create redirect login links
Use a key with login_links:manage. The identity must be active and already have access to the requested project or location, the workspace must have hosted white-label enabled, and the assigned package must be active.
curl --request POST \
--url 'https://api.signal.ceyo.ai/v1/login-links' \
--header "Authorization: Bearer ${SIGNAL_API_KEY}" \
--header 'Content-Type: application/json' \
--data '{
"external_user_id": "customer-user-42",
"project_id": "customer-project",
"expires_in": 900
}'expires_in defaults to 900 seconds and accepts 60 to 3,600 seconds.login_link.url directly to the intended customer. Redemption marks it used, so it cannot be redeemed again.landing and redirect_path to open the selected project or location. Set landing to auto, projects, or locations for common landing pages. Use redirect_path alone for an advanced internal page.return_url must use HTTPS and an origin listed in the issuing API key's allowed_origins.GET /login-links/{login_link_id} to inspect status. Use DELETE on the same path to revoke a link while it is still pending. Treat the returned URL as a secret.Manage session renewal and revocation
Embed sessions default to 3,600 seconds and accept a TTL from 60 to 86,400 seconds. Refresh them through your backend; never expose the API key to the browser.
curl --request POST \
--url 'https://api.signal.ceyo.ai/v1/embed/sessions/refresh' \
--header "Authorization: Bearer ${SIGNAL_API_KEY}" \
--header 'Content-Type: application/json' \
--header "Idempotency-Key: ${UNIQUE_REQUEST_ID}" \
--data '{
"session_token": "${CURRENT_SESSION_TOKEN}",
"ttl_seconds": 3600
}'onTokenExpired callback. Return the replacement token, or call embed.updateToken() after your backend refreshes it.Idempotency-Key. Reuse it only when retrying the same operation with the same request body.