Embed SignStack into Your App

This guide is the practical companion to Embedding Components. You'll wire up a working signing flow inside your own app — server-side embed-token issuance, browser-side mounting of the web component, and event handling.

The same pattern applies to all four components (<ss-signing-embed>, <ss-resource-editor>, <ss-workflow-editor>, <ss-workflow-monitor>); the differences are which intent you mint a token for and which props each component takes.

The Pattern

  1. Your server holds the long-lived API key. When a logged-in user wants to access an embedded surface (e.g. sign a document), your server calls SignStack's POST /v1/orgs/{orgId}/namespaces/{namespaceKey}/auth/embed to mint a short-lived embed token scoped to that user + intent.
  2. Your server returns the token to the browser.
  3. The browser mounts the SignStack web component and passes the token in.
  4. The component uses the token for its own API calls; you listen for events (signed, declined, session-expired).

Step 1: Load the Web Components Bundle

Drop the SignStack web components script into your app's HTML. Once loaded, the four custom elements register themselves and you can use them anywhere in your DOM:

<script type="module" src="https://signstack.ai/web-components.js"></script>

Step 2: Mint an Embed Token (Server-Side)

For an embedded signing flow:

curl -X POST https://api.signstack.ai/v1/orgs/{orgId}/namespaces/{namespaceKey}/auth/embed \
  -H "Authorization: Bearer <ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  -d '{
    "intent": "signing_session",
    "workflowId": "b6f3a8d2-1c4e-4b9a-a7f3-2e8c1d5a9b4f",
    "stepKey": "consultant_signs",
    "expiresIn": 900,
    "allowedOrigins": ["https://yourapp.com"],
    "context": {
      "recipientEmail": "jane@example.com",
      "recipientName": "Jane Doe"
    }
  }'

Response:

{
  "accessToken": "eyJhbGciOi…",
  "orgId": "f1e2d3c4-b5a6-4789-a012-345678901234",
  "tokenType": "Bearer",
  "expiresIn": 900,
  "expiresAt": "2026-04-19T16:30:00.000Z",
  "scopes": ["..."],
  "subject": { "type": "embed", "id": "8a9b0c1d-2e3f-4a5b-6c7d-8e9f0a1b2c3d", "orgId": "f1e2d3c4-b5a6-4789-a012-345678901234" },
  "resources": [{ "type": "workflow", "id": "b6f3a8d2-1c4e-4b9a-a7f3-2e8c1d5a9b4f", "actions": ["sign"] }]
}

accessToken is what the browser receives. Pass allowedOrigins to scope the token to your domain (browser CORS protection); set expiresIn to the shortest reasonable window for your UX.

The full intent catalog and security rationale lives in Securely Embedding Components.

Step 3: Mount the Component (Browser-Side)

<ss-signing-embed
  workflow-id="b6f3a8d2-1c4e-4b9a-a7f3-2e8c1d5a9b4f"
  embed-token="eyJhbGciOi…"
  org-id="f1e2d3c4-b5a6-4789-a012-345678901234"
  namespace-key="acme-prod"
></ss-signing-embed>

In React (custom elements work natively):

function SigningPage({ workflowId, embedToken, orgId, namespaceKey }) {
  const ref = useRef(null);

  useEffect(() => {
    const el = ref.current;
    const onSigned   = (e) => console.log('signed', e.detail);
    const onDeclined = (e) => console.log('declined', e.detail);
    const onExpired  = () => refetchEmbedToken();
    el.addEventListener('signed', onSigned);
    el.addEventListener('declined', onDeclined);
    el.addEventListener('session-expired', onExpired);
    return () => {
      el.removeEventListener('signed', onSigned);
      el.removeEventListener('declined', onDeclined);
      el.removeEventListener('session-expired', onExpired);
    };
  }, []);

  return (
    <ss-signing-embed
      ref={ref}
      workflow-id={workflowId}
      embed-token={embedToken}
      org-id={orgId}
      namespace-key={namespaceKey}
    />
  );
}

The component picks up your app's CSS for typography, colors, and surrounding chrome — no theme-override config to manage.

Step 4: Handle Token Expiry

Embed tokens are deliberately short-lived. When one expires, the component fires a session-expired event. Your handler should call your server to mint a fresh token and update the component's embed-token attribute. The component will reconnect with the new token.

Component Reference

All four components share embed-token, org-id, namespace-key. Per-component specifics:

Component Intent (token mint) Required props Key events
<ss-signing-embed> signing_session workflow-id signed, declined, signing-error, session-expired, closed
<ss-workflow-editor> workflow_editing workflow-id saved, published, session-expired
<ss-workflow-monitor> workflow_monitoring (uses namespace-scoped list) workflow-selected, session-expired
<ss-resource-editor> resource_editing resource-key, resource-kind, version? saved, published, session-expired

(The signing component runs the actual signature capture in a sandboxed context for security; the others render inline within your DOM.)

Common Pitfalls

  • Forgetting allowedOrigins — Tokens are usable from any browser without it. Always pass your app's exact origin(s).
  • Reusing tokens across users — Each token is scoped to one user/intent. Mint per-session; don't cache server-side.
  • Long expiresIn — Default to 15 minutes. Long-lived embed tokens defeat the security model.
  • Treating events as authoritative — Always confirm signing completion via webhooks or workflow status; the browser event is a UX signal, not a source of truth.