DOCS
Embed token
Tokens HMAC, prefixo sveda_embed_, escopo visitor_id. O host faz mint; o navegador só envia.
Embed token
Tokens HMAC, prefixo sveda_embed_, escopo visitor_id. O host faz mint; o navegador só envia.
POST /sveda/embed/token, x-sveda-embed-token, Authorization Bearer, host_mcp_url com host_mcp_token.
POST /sveda/embed/token
Enable with
SVEDA_EMBED_ENABLED=true
or the route returns 404.
Request body
EmbedTokenRequest:
visitor_id,
host_mcp_url,
host_mcp_token,
optional
policy
(string name), optional
grants
(object that can only tighten the named policy).
Empty
visitor_id
becomes a UUID (max 64 chars).
Response
EmbedTokenResponse:
token,
visitor_id,
expires_in
(seconds; default TTL 3600 via
SVEDA_EMBED_TOKEN_TTL,
minimum 60).
Token prefix
sveda_embed_.
End-user browsers and mobile apps must receive the short-lived embed token from the host backend. They must not hold
SVEDA_EMBED_HOST_API_KEY.
POST /sveda/embed/token
Content-Type: application/json
x-sveda-host-key: $SVEDA_EMBED_HOST_API_KEY
{
"visitor_id": "visitor-123",
"host_mcp_url": "https://app.example.com/mcp",
"host_mcp_token": "host-mcp-secret",
"policy": "reader",
"grants": {
"mcp": { "allow": ["tickets.search"], "max_mode": "read" }
}
}POST /sveda/embed/token
Content-Type: application/json
x-sveda-host-key: $SVEDA_EMBED_HOST_API_KEY
{
"visitor_id": "visitor-123",
"host_mcp_url": "https://app.example.com/mcp",
"host_mcp_token": "host-mcp-secret",
"policy": "reader",
"grants": {
"mcp": { "allow": ["tickets.search"], "max_mode": "read" }
}
} {
"token": "sveda_embed_…",
"visitor_id": "visitor-123",
"expires_in": 3600
}{
"token": "sveda_embed_…",
"visitor_id": "visitor-123",
"expires_in": 3600
} const res = await fetch('http://127.0.0.1:8787/sveda/embed/token', {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-sveda-host-key': process.env.SVEDA_EMBED_HOST_API_KEY ?? '',
},
body: JSON.stringify({
visitor_id: 'visitor-123',
policy: 'reader',
}),
})
const { token, visitor_id, expires_in } = await res.json()const res = await fetch('http://127.0.0.1:8787/sveda/embed/token', {
method: 'POST',
headers: {
'content-type': 'application/json',
'x-sveda-host-key': process.env.SVEDA_EMBED_HOST_API_KEY ?? '',
},
body: JSON.stringify({
visitor_id: 'visitor-123',
policy: 'reader',
}),
})
const { token, visitor_id, expires_in } = await res.json() Policies and capabilities
Sveda does not store application users or roles. The host app maps its own user or role to a policy name when minting an embed token.
- Role — lives in the host app (Spatie, Django groups, and so on).
-
Policy — a named entry under
policiesinsveda.yaml. -
Capability — enforced on every chat turn from the token's policy plus optional
grants.
Unknown policy name returns 422. Tokens without
policy
/
grants
keep the old unrestricted behavior. Substituting an email in the browser does not change policy; the host reads the authenticated user from its own session.
One named policy, as it sits under
policies
in
sveda.yaml.
Globs are only
*,
prefix*,
*suffix
(no regex).
max_mode
order:
read
<
write
<
delete.
When a policy is set, omitted
web
/
code
default to denied, and empty
mcp.allow
/
client.allow
means no tools of that class.
policies:
reader:
web: false
code: false
mcp:
allow: ["tickets.*"]
domains: ["tickets"]
max_mode: read
client:
allow: []policies:
reader:
web: false
code: false
mcp:
allow: ["tickets.*"]
domains: ["tickets"]
max_mode: read
client:
allow: [] GET /sveda/embed/config
returns
capabilities
for UI hints. Enforcement is the token, not the UI. Row-level data rules stay in the host tool handler.
GET /sveda/embed/config
x-sveda-embed-token: sveda_embed_…
{
"capabilities": {
"web": false,
"code": false,
"mcp": { "allow": ["tickets.*"], "domains": ["tickets"], "max_mode": "read" },
"client": { "allow": [] }
}
}GET /sveda/embed/config
x-sveda-embed-token: sveda_embed_…
{
"capabilities": {
"web": false,
"code": false,
"mcp": { "allow": ["tickets.*"], "domains": ["tickets"], "max_mode": "read" },
"client": { "allow": [] }
}
}
Host SDKs map the signed-in user with
policyUsing
(and optionally
resolveToolsUsing
as a second host-side filter). See
Node
and
Laravel.
Visitor header
Stream, message, histories, and document extract authenticate the visitor with the embed token. Prefer
x-sveda-embed-token.
Authorization: Bearer sveda_embed_…
is accepted when the value starts with
sveda_embed_.
x-sveda-embed-token: sveda_embed_…
Authorization: Bearer sveda_embed_…x-sveda-embed-token: sveda_embed_… Authorization: Bearer sveda_embed_…
Host key
If
SVEDA_EMBED_HOST_API_KEY
is unset, minting does not require a host key.
If it is set, send
x-sveda-host-key
or
Authorization: Bearer
with a value that is not a
sveda_embed_
token. Missing or mismatched key returns 401.
URL + token pairing
host_mcp_url
and
host_mcp_token
must be paired. One without the other is 422.
The URL must be
http://
or
https://
only.
Stored MCP creds are keyed by
visitor_id
and only persist when a host API key is configured.
Call flow:
MCP and tools.
Product:
embed tokens.
Mint from a host SDK
Do not call this route from the browser. Language SDKs wrap
POST /sveda/embed/token
and expose
POST /sveda/session
to the page. Numbered install:
Related
Host SDKs
Language mint helpers and POST /sveda/session.
No framework
HTML, iframe, tiny HTTP.
Get started
Mint, then session.send.
Policies
Role on the host, policy name on the token.
MCP and tools
Host MCP handshake and policy filters.
JS client
headers: { x-sveda-embed-token }.
Appearance
Host JS wins; sveda.yaml if omitted.
Product: host SDKs
Why minting stays on the server.