Glozr docs

API reference

Signed CTA context

Signed CTA context lets you forward visitor data to your CTA destinations through cryptographically signed URL parameters. When a CTA is configured with forward context, three query parameters are appended to the destination URL — and your backend can verify they really came from Glozr.

URL structure

The outbound URL follows this pattern:

{base-url}?pitchbar_ctx=<base64url-json>
         &pitchbar_ts=<unix-seconds>
         &pitchbar_sig=<hex-hmac-sha256>

Each parameter serves a specific purpose: pitchbar_ctx carries the visitor payload, pitchbar_ts enables replay protection, and pitchbar_sig proves the URL was minted by Glozr.

Whitelisted fields

You can selectively forward any of the following:

  • conversation_id — the active conversation.
  • agent_id — the owning agent.
  • page_url — the visitor's current page.
  • visitor_email — the latest captured email.
  • visitor_name — the latest captured name.
  • captured_fields — custom lead fields, with sensitive keys automatically stripped.

Security measures

  • HMAC-SHA256 signature using a workspace-scoped secret known only to Glozr and your server.
  • Automatic filtering of sensitive field names — any field name matching password, token, secret or api_key is stripped before signing.
  • Five-minute replay window — URLs older than 300 seconds are rejected on verification.
  • Constant-time comparison — both sides use timing-safe equality to resist signature-recovery attacks.

Note. The workspace secret must stay server-side. Never expose it in browser code, mobile apps or any client environment a user can inspect.

Verification

PHP:

function verify_glozr_ctx(array $query, string $secret): ?array {
    $ctx = $query['pitchbar_ctx'] ?? null;
    $ts  = (int)($query['pitchbar_ts']  ?? 0);
    $sig = $query['pitchbar_sig'] ?? '';

    if (!$ctx || !$ts || !$sig) return null;
    if (abs(time() - $ts) > 300) return null;

    $expected = hash_hmac('sha256', "{$ts}.{$ctx}", $secret);
    if (!hash_equals($expected, $sig)) return null;

    return json_decode(base64_decode(strtr($ctx, '-_', '+/')), true);
}

Node.js:

const crypto = require('crypto');

function verify(query, secret) {
  const { pitchbar_ctx: ctx, pitchbar_ts: ts, pitchbar_sig: sig } = query;
  if (!ctx || !ts || !sig) return null;
  if (Math.abs(Date.now()/1000 - Number(ts)) > 300) return null;

  const expected = crypto.createHmac('sha256', secret)
    .update(`${ts}.${ctx}`).digest('hex');
  if (!crypto.timingSafeEqual(Buffer.from(expected,'hex'), Buffer.from(sig,'hex'))) return null;

  return JSON.parse(Buffer.from(ctx, 'base64url').toString());
}

Python:

import base64, hmac, hashlib, json, time

def verify(query, secret):
    ctx = query.get('pitchbar_ctx')
    ts  = int(query.get('pitchbar_ts') or 0)
    sig = query.get('pitchbar_sig', '')
    if not (ctx and ts and sig):
        return None
    if abs(time.time() - ts) > 300:
        return None

    expected = hmac.new(secret.encode(),
                        f"{ts}.{ctx}".encode(),
                        hashlib.sha256).hexdigest()
    if not hmac.compare_digest(expected, sig):
        return None

    padded = ctx + '=' * (-len(ctx) % 4)
    return json.loads(base64.urlsafe_b64decode(padded))

Stacked CTA cards

An agent can carry several CTA rules, each with its own priority integer. When more than one matches the same reply, the widget renders them together as a stacked list of cards — for example Pricing, Demo and Docs side by side — rather than just the single top match. CTAs are evaluated in descending priority order against the page URL, conversation language and the assistant's text, and the matches are returned in that order so the highest-priority card sits first.

A hard cap of three cards per reply applies (MAX_CTAS = 3): once three matching CTAs are collected, evaluation stops. The streamed done event carries the full set as ctas; a singular cta field is still populated with the first match so older widget bundles and JSON-fallback clients always render at least one card. Signed-context forwarding is applied per card, so each CTA can opt into its own whitelisted payload independently.