Skip to main content

Overview

The Handshake endpoint is called every time a user opens the supplies module in your platform. It authenticates the user and returns a secure, time-limited session token that you use to embed the Sticker application.

When to Call This Endpoint

1

User Navigation

User clicks on “Supplies” or similar navigation item in your platform
2

Identify User

Get the current authenticated user’s details from your session
3

Call Handshake API

Send user information to receive a session token
4

Embed iframe

Display the iframe with the session token in the URL

API Endpoint

POST /api/partner/handshake
Base URL: https://api.sticker.com (production) or https://sandbox.api.sticker.com (sandbox)

Authentication

This endpoint requires both API key authentication and request signing:
headers: {
  'Authorization': 'Bearer sk_live_your_api_key',
  'Content-Type': 'application/json',
  'X-Partner-Signature': 'sha256=hmac_digest_here'
}
The handshake endpoint requires HMAC-SHA256 request signing for security. See the Authentication Guide for details.

Request Format

{
  "partner_org_id": "your_internal_org_id",
  "user": {
    "email": "dr.smith@acmemedical.com",
    "first_name": "John",
    "last_name": "Smith"
  }
}

Request Parameters

partner_org_id
string
required
Your internal identifier for the organization. This must match the ID used during organization setup.
user.email
string
required
The user’s email address. Must match an email in the organization’s user list.
user.first_name
string
required
The user’s first name (used to update profile if changed)
user.last_name
string
required
The user’s last name (used to update profile if changed)

Generating Request Signatures

The handshake endpoint requires an HMAC-SHA256 signature to verify request authenticity:

Signature Algorithm

1

Serialize Request Body

Convert your request to a JSON string (no pretty printing)
const requestBody = JSON.stringify({
  partner_org_id: 'org_123',
  user: {
    email: 'user@example.com',
    first_name: 'John',
    last_name: 'Doe'
  }
});
2

Create HMAC Digest

Hash the request body using your API key as the secret
const crypto = require('crypto');

const hmac = crypto
  .createHmac('sha256', process.env.STICKER_API_KEY)
  .update(requestBody)
  .digest('hex');
3

Format Signature

Add the “sha256=” prefix
const signature = `sha256=${hmac}`;
4

Add to Headers

Include in the X-Partner-Signature header
headers: {
  'X-Partner-Signature': signature
}

Complete Signing Example

const crypto = require('crypto');

function generateSignature(requestBody, apiKey) {
  const hmac = crypto
    .createHmac('sha256', apiKey)
    .update(requestBody)
    .digest('hex');
  
  return `sha256=${hmac}`;
}

async function handshakeUser(orgId, user) {
  const requestBody = JSON.stringify({
    partner_org_id: orgId,
    user: {
      email: user.email,
      first_name: user.firstName,
      last_name: user.lastName
    }
  });
  
  const signature = generateSignature(
    requestBody,
    process.env.STICKER_API_KEY
  );
  
  const response = await fetch('https://api.sticker.com/api/partner/handshake', {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${process.env.STICKER_API_KEY}`,
      'Content-Type': 'application/json',
      'X-Partner-Signature': signature
    },
    body: requestBody
  });
  
  if (!response.ok) {
    const error = await response.json();
    throw new Error(`Handshake failed: ${error.error}`);
  }
  
  return await response.json();
}

Response Format

Success Response (200 OK)

{
  "success": true,
  "session_key": "a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6",
  "iframe_url": "https://app.sticker.com/embed",
  "expires_in": 300
}
success
boolean
Whether the handshake succeeded
session_key
string
Secure, time-limited token for authenticating the user in the iframe. Single-use only.
iframe_url
string
Base URL for embedding Sticker. Append ?session_key={session_key} to this URL.
expires_in
number
Time in seconds until the session token expires (default: 300 seconds / 5 minutes)

Error Responses

{
  "error": "Missing required field: user.email",
  "code": "INVALID_REQUEST"
}
Common causes:
  • Missing required fields (partner_org_id, user.email, etc.)
  • Invalid email format
  • Malformed request body
{
  "error": "Invalid signature",
  "code": "INVALID_SIGNATURE"
}
Solution: Verify your HMAC signature is generated correctly using the exact request body
{
  "error": "No profile found with partner_org_id: org_123",
  "code": "ORGANIZATION_NOT_FOUND"
}
Solution: Ensure the organization was set up via the organization-setup endpoint first
{
  "error": "User not authorized for this organization",
  "code": "USER_NOT_FOUND"
}
Solution: The user email doesn’t exist in this organization. Add the user via organization-setup endpoint.
{
  "error": "Rate limit exceeded",
  "code": "RATE_LIMIT_EXCEEDED",
  "retry_after": 60
}
Solution: Implement exponential backoff and retry after the specified seconds

Session Token Properties

Understanding session tokens is crucial for secure integration:

Single-Use

Each token can only be used once to authenticate

Time-Limited

Tokens expire 5 minutes after generation

User-Specific

Tokens are tied to a specific user and organization

Secure

Cryptographically random, 64-character hex string
Never reuse session tokens. Generate a new token each time the user opens the supplies module, even if they just closed it seconds ago.

Using the Session Token

After receiving the session token, append it to the iframe URL:
<iframe
  src="https://app.sticker.com/embed?session_key=a1b2c3d4e5f6g7h8i9j0..."
  width="100%"
  height="800px"
  frameborder="0"
/>
See the iframe Embedding Guide for complete implementation details.

Handling Name Updates

The handshake endpoint automatically updates user profiles if names have changed:
// User changed their name in your system
const updatedUser = {
  email: 'dr.smith@acmemedical.com',
  first_name: 'Jonathan', // Changed from 'John'
  last_name: 'Smith'
};

// Next handshake will update the name in Sticker
const result = await handshakeUser(orgId, updatedUser);
Sticker automatically syncs user first and last names on each handshake. Email addresses cannot be changed (they’re the unique identifier).

Session Management Best Practices

Create session tokens only when the user clicks to open the supplies module, not in advance.
If a user takes more than 5 minutes to load the page, generate a fresh token.
Don’t cache or store session tokens. They’re single-use and short-lived.
If iframe fails to load, generate a new token and retry.
Show loading states while generating tokens and embedding the iframe.

Complete Integration Example

Here’s a full implementation including error handling and loading states:
import { useState, useEffect } from 'react';

function SuppliesModule({ user, organizationId }) {
  const [sessionKey, setSessionKey] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    async function authenticate() {
      try {
        setLoading(true);
        setError(null);
        
        // Call your backend to get session token
        const response = await fetch('/api/supplies/authenticate', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({
            org_id: organizationId,
            user: {
              email: user.email,
              first_name: user.firstName,
              last_name: user.lastName
            }
          })
        });
        
        if (!response.ok) {
          throw new Error('Authentication failed');
        }
        
        const data = await response.json();
        setSessionKey(data.session_key);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }
    
    authenticate();
  }, [user, organizationId]);

  if (loading) {
    return <div>Loading supplies...</div>;
  }

  if (error) {
    return (
      <div>
        <p>Failed to load supplies: {error}</p>
        <button onClick={() => window.location.reload()}>
          Retry
        </button>
      </div>
    );
  }

  return (
    <iframe
      src={`https://app.sticker.com/embed?session_key=${sessionKey}`}
      width="100%"
      height="800px"
      frameBorder="0"
      allow="payment"
      sandbox="allow-same-origin allow-scripts allow-forms allow-popups"
      style={{ border: 'none' }}
    />
  );
}

Testing

Test your handshake implementation in the sandbox:
# Sandbox endpoint
POST https://sandbox.api.sticker.com/api/partner/handshake

# Test with a previously created organization
{
  "partner_org_id": "test_org_123",
  "user": {
    "email": "test@example.com",
    "first_name": "Test",
    "last_name": "User"
  }
}
Use browser DevTools Network tab to inspect handshake requests and verify signatures are being generated correctly.

Next Steps