Docs

Embed StrykePoint in your app

Render Inbox, Composer, Scout, and Sequences inside any host app. Your server mints a short-lived signed token; StrykePoint mints a scoped session cookie. No second login.

Signed JWT SSO

HMAC-SHA256, max 5 min, single-use jti, origin-locked.

Drop-in iframe

One <script> tag, one mount() call.

postMessage bridge

Events for ready, unread counts, reply sent, navigate.

1Turn on the add-on

In StrykePoint, open Admin → Embed, enable the add-on, then create a key. Name it, paste your host origin(s) (e.g. https://your-app.com), and pick the surfaces you want available (inbox, composer, scout, sequences). You'll see the secret once — store it in your server's secret manager.

2Mint a token on your server

Tokens are short-lived (5 min) and single-use. The secret never leaves your server.

js
// Node / TanStack / any JS edge runtime
import { createHmac } from "node:crypto";

function b64url(s) {
  return Buffer.from(s).toString("base64")
    .replace(/=+$/g, "").replace(/\+/g, "-").replace(/\//g, "_");
}

export function mintEmbedToken({ publishableKey, secret, user, scopes }) {
  const header = { alg: "HS256", typ: "JWT" };
  const now = Math.floor(Date.now() / 1000);
  const payload = {
    pk: publishableKey,
    sub: user.id,                 // your stable user id
    email: user.email,
    name: user.name,
    scope: scopes,                // ["inbox","composer","scout","sequences"]
    aud: "strykepoint",
    iss: "https://your-app.com",
    iat: now,
    exp: now + 5 * 60,            // max 5 minutes
    jti: crypto.randomUUID(),     // single-use
  };
  const h = b64url(JSON.stringify(header));
  const p = b64url(JSON.stringify(payload));
  const sig = createHmac("sha256", secret).update(`${h}.${p}`).digest();
  const s = sig.toString("base64")
    .replace(/=+$/g, "").replace(/\+/g, "-").replace(/\//g, "_");
    return `${h}.${p}.${s}`;
}
py
# Python
import time, uuid, hmac, hashlib, base64, json

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

def mint_embed_token(pk: str, secret: str, user: dict, scopes: list[str]) -> str:
    header = {"alg":"HS256","typ":"JWT"}
    now = int(time.time())
    payload = {
      "pk": pk, "sub": user["id"], "email": user.get("email"),
      "name": user.get("name"), "scope": scopes,
      "aud": "strykepoint", "iss": "https://your-app.com",
      "iat": now, "exp": now + 300, "jti": str(uuid.uuid4()),
    }
    h = b64url(json.dumps(header, separators=(",",":")).encode())
    p = b64url(json.dumps(payload, separators=(",",":")).encode())
    sig = hmac.new(secret.encode(), f"{h}.{p}".encode(), hashlib.sha256).digest()
    return f"{h}.{p}.{b64url(sig)}"

3Drop the panel into your page

Pass the token your server minted into Strykepoint.mount(). The SDK builds the iframe and wires up the postMessage bridge.

html
<div id="strykepoint-panel" style="height:600px"></div>
<script src="https://app.strykepoint.ai/embed/sdk.js"></script>
<script>
  // `token` comes from your server (never expose the secret to the browser).
  const panel = Strykepoint.mount('#strykepoint-panel', {
    publishableKey: 'pk_live_...',
    token: window.__SP_TOKEN__,
    surface: 'inbox',           // inbox | composer | scout | sequences
    onEvent: (e) => {
      if (e.type === 'ready')        console.log('embed ready', e.scopes);
      if (e.type === 'unread_count') console.log('unread', e.n);
      if (e.type === 'reply_sent')   console.log('replied', e.threadId);
    },
  });
  // panel.open('thread_123'); panel.refresh(); panel.destroy();
</script>

4Token claims

FieldTypeNotes
pkstringPublishable key from the embed key page.
substringYour stable user id for this end-user.
emailstring?Optional. Used to populate the embed identity.
namestring?Optional display name.
scopestring[]Subset of the key's scopes to grant this token.
audstringMust equal "strykepoint".
issstringYour host origin, e.g. https://your-app.com.
expnumberUnix seconds, ≤ now + 300.
jtistringUnique per token (UUID). Replay-protected.
wsstring?Optional workspace override; must match the key's workspace.

5Events from the embed

FieldTypeNotes
ready{ surface, scopes }Fires once after the bridge connects.
unread_count{ n }New unread DMs across connected channels.
reply_sent{ threadId }User sent a reply from the embed.
navigate{ to }User asked to open something outside the embed.

6Sending commands into the embed

The handle returned from mount() exposes open(id), refresh(), send(msg), and destroy(). Under the hood these postMessage to the iframe with { source: "strykepoint-host", ... }.

7Read-only HMAC API (badges without an iframe)

For badge counts and lightweight server-to-server reads you don't need to render the iframe. Sign each request with the same secret. Send these headers:

FieldTypeNotes
x-sp-keystringYour publishable key (pk_live_...).
x-sp-tsnumberUnix seconds; ±5 min clock skew allowed.
x-sp-sighexHMAC-SHA256 of `${ts}\n${METHOD}\n${path}\n${bodyHash}` using the secret.

bodyHash is HMAC-SHA256("strykepoint.embed.body", rawBody) in hex ("" for GET).

js
// Sign a read-only API request (Node)
import { createHmac } from "node:crypto";

export function signEmbedRequest({ secret, method, path, body = "" }) {
  const ts = Math.floor(Date.now() / 1000).toString();
  const bodyHash = createHmac("sha256", "strykepoint.embed.body")
    .update(body).digest("hex");
  const msg = `${ts}\n${method.toUpperCase()}\n${path}\n${bodyHash}`;
  const sig = createHmac("sha256", secret).update(msg).digest("hex");
  return { ts, sig };
}

// Usage:
const { ts, sig } = signEmbedRequest({
  secret: process.env.SP_EMBED_SECRET,
  method: "GET",
  path: "/api/public/embed/v1/inbox/unread-count",
});
await fetch("https://app.strykepoint.ai/api/public/embed/v1/inbox/unread-count", {
  headers: { "x-sp-key": "pk_live_...", "x-sp-ts": ts, "x-sp-sig": sig },
});

Available endpoints:

FieldTypeNotes
GET /v1/health—Sanity check. Returns workspace id, scopes, server time.
GET /v1/inbox/unread-countscope: inboxReturns { n, window } — replies received in the last 14 days.

8Security checklist

  • Mint tokens server-side only. The HMAC secret must never reach the browser.
  • Keep exp ≤ 5 minutes and a fresh jti per token.
  • Lock allowed origins on the key. Unknown origins are rejected at exchange.
  • Request only the scopes you need; the embed enforces per-surface scope.
  • Rotate the secret from the Embed admin page if a host environment is compromised.