Skip to content

Server-side token generation (SSO)

The Trip Planner Widget, SSO Button Widget, and Headless SDK request a signed token from your backend. Traveln verifies it on /sso-login/ and creates a tenant session.

Token endpoint

Implement a POST endpoint (for example /api/traveln/sso/) that returns:

{ "token": "..." }

Request payloads

The two web embeds send slightly different client payloads to your token endpoint.

Trip Planner Widget request

{ "prompt": "string", "trace_id": "string" }

SSO Button Widget request

{ "trace_id": "string" }

Headless SDK request

{ "prompt": "string", "trace_id": "string" }

The SDK sends prompt only when the launch action needs it. Accommodation filters and redirect targets are forwarded during the /sso-login/ redirect, not to your token endpoint.

You can ignore fields you do not need. The button target itself is controlled by data-button-type on the Traveln loader and is forwarded by Traveln during the /sso-login/ redirect.

Token format

This is not a JWT.

The token is:

base64url(payload_json_bytes) + "." + base64url(hmac_sha256(secret, payload_json_bytes))

Required payload fields

  • tenant_slug (string)
  • user_id (string)
  • ts (integer, unix seconds)
  • nonce (string, unique per token)

Optional payload fields

  • first_name, last_name, email, phone, picture
  • host (string): when present, Traveln binds the token to both the request host and the token host (both must be registered for the tenant).
  • is_anonymous (bool/string): when true or identity is missing, Traveln may create a "Guest User" session.

Identity modes

Mode Payload guidance Traveln behavior
Known user Send stable user_id plus available first_name, last_name, email, phone, and picture. Creates a non-anonymous SILENT session. Tenant UI can show account controls.
Guest or anonymous Send is_anonymous: true or omit required identity fields. Creates a guest session and marks it as anonymous for tenant UI.

For known users, prefer a stable partner user identifier in user_id. For anonymous users, still send a unique user_id and one-time nonce; do not reuse SSO tokens.

Replay protection

Nonces are one-time use and timestamps are short-lived. Do not reuse tokens.

Example (Python)

import base64
import hmac
import json
import time
from hashlib import sha256
from uuid import uuid4

def b64url(data: bytes) -> str:
    return base64.urlsafe_b64encode(data).decode("utf-8").rstrip("=")

def create_traveln_sso_token(*, tenant_slug: str, user_id: str, secret: str, host: str | None = None) -> str:
    payload = {
        "tenant_slug": tenant_slug,
        "user_id": user_id,
        "ts": int(time.time()),
        "nonce": uuid4().hex,
    }
    if host:
        payload["host"] = host

    payload_bytes = json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
    sig = hmac.new(secret.encode("utf-8"), payload_bytes, sha256).digest()
    return f"{b64url(payload_bytes)}.{b64url(sig)}"

Example with user identity

payload = {
    "tenant_slug": "your-tenant-slug",
    "host": "partner.example.com",
    "user_id": "partner-user-123",
    "first_name": "Amina",
    "last_name": "Hassan",
    "email": "amina@example.com",
    "phone": "+201000000000",
    "ts": int(time.time()),
    "nonce": uuid4().hex,
}

Example anonymous payload

payload = {
    "tenant_slug": "your-tenant-slug",
    "host": "partner.example.com",
    "user_id": "guest-session-123",
    "is_anonymous": True,
    "ts": int(time.time()),
    "nonce": uuid4().hex,
}

Runtime behavior after token creation

Once your endpoint returns { "token": "..." }:

  1. Traveln receives the signed token.
  2. Traveln verifies the tenant, timestamp, nonce, and HMAC signature.
  3. Traveln creates a local session for the user.
  4. Traveln redirects the user to the destination requested by the widget flow:
  5. Trip planner widget -> /ai-trip-planner/
  6. Button widget with trip_planner -> /ai-trip-planner/
  7. Button widget with trips -> /trips/
  8. Button widget with accommodation_search -> /accommodation-search/
  9. Headless SDK targets -> the matching planner, trips, or accommodation destination