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 iframe.
When to Call This Endpoint
User Clicks Supplies
User navigates to the supplies/procurement section in your platform
Get Current User
Identify the authenticated user from your session
Call Handshake API
Send user identifier to receive a session token
Embed iframe
Display the iframe using the returned URL
API Endpoint
POST /v1/partner/handshake
Base URL: https://api.usesticker.com
Authentication
Use the X-API-Key header (not Authorization: Bearer):
X-API-Key: sk_live_your_api_key_here
Send either internal_user_id OR profile_id:
{
"internal_user_id" : "user-789"
}
OR:
{
"profile_id" : "660f9500-f30c-52e5-b827-557766551111"
}
Recommended: Use internal_user_id (your internal identifier from organization setup). This way you don’t need to store Sticker profile IDs in your database.
{
"success" : true ,
"session_key" : "a1b2c3d4e5f6789012345678901234567890123456789012345678901234" ,
"iframe_embed_url" : "https://shop.usesticker.com/embedded/550e8400-e29b-41d4-a716-446655440000?session_key=a1b2c3d4e5f6789012345678901234567890123456789012345678901234" ,
"expires_at" : "2024-01-15T10:35:00.000Z" ,
"profile" : {
"id" : "660f9500-f30c-52e5-b827-557766551111" ,
"first_name" : "Jane" ,
"last_name" : "Smith" ,
"email" : "jane@acmemedical.com"
}
}
Response Fields
Field Description session_key64-char hex token for authentication iframe_embed_urlComplete URL to embed—use this directly! expires_atWhen the token expires (5 minutes from creation) profileBasic info about the authenticated user
Session Token Properties
Session tokens have important security properties:
Property Description Single-use Token is invalidated after first use 5 minute expiry Token expires 5 minutes after creation User-bound Tied to a specific user profile Partner-bound Can only be used with your partner iframe
Never reuse session tokens. Generate a new one each time the user opens supplies, even if they closed it seconds ago.
Complete Flow Example
Here’s what a typical integration looks like:
Code Examples
Your Backend Endpoint
Node.js/Express
Python/FastAPI
cURL
app . post ( '/api/supplies/auth' , async ( req , res ) => {
const { userId } = req . body ;
// Verify user is authenticated in YOUR system
const currentUser = await getAuthenticatedUser ( req );
if ( ! currentUser || currentUser . id !== userId ) {
return res . status ( 401 ). json ({ error: 'Unauthorized' });
}
// Call Sticker handshake
const response = await fetch ( 'https://api.usesticker.com/v1/partner/handshake' , {
method: 'POST' ,
headers: {
'X-API-Key' : process . env . STICKER_API_KEY ,
'Content-Type' : 'application/json' ,
},
body: JSON . stringify ({
internal_user_id: userId
})
});
if ( ! response . ok ) {
const error = await response . json ();
console . error ( 'Handshake failed:' , error );
return res . status ( response . status ). json ({ error: 'Authentication failed' });
}
const data = await response . json ();
// Return the iframe URL to your frontend
res . json ({
iframe_url: data . iframe_embed_url ,
expires_at: data . expires_at
});
});
Your Frontend Component
// React component that loads the supplies iframe
import { useState , useEffect } from 'react' ;
function SuppliesModule ({ userId }) {
const [ iframeUrl , setIframeUrl ] = useState ( null );
const [ loading , setLoading ] = useState ( true );
const [ error , setError ] = useState ( null );
useEffect (() => {
async function authenticate () {
try {
setLoading ( true );
setError ( null );
// Call YOUR backend (not Sticker directly!)
const response = await fetch ( '/api/supplies/auth' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ({ userId })
});
if ( ! response . ok ) {
throw new Error ( 'Failed to authenticate' );
}
const { iframe_url } = await response . json ();
setIframeUrl ( iframe_url );
} catch ( err ) {
setError ( err . message );
} finally {
setLoading ( false );
}
}
authenticate ();
}, [ userId ]);
if ( loading ) return < LoadingSpinner /> ;
if ( error ) return < ErrorMessage error = { error } onRetry = { () => window . location . reload () } /> ;
return (
< iframe
src = { iframeUrl }
className = "w-full h-full border-0"
title = "Sticker Embedded Procurement"
sandbox = "allow-same-origin allow-scripts allow-forms allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation"
allow = "payment; publickey-credentials-get; fullscreen"
/>
);
}
Error Handling
{
"error" : "Invalid request body" ,
"details" : [
{
"message" : "Either internal_user_id or profile_id must be provided"
}
]
}
Solution: Include either internal_user_id or profile_id in the request body.
{
"error" : "Unauthorized" ,
"message" : "Invalid or missing API key"
}
Solution: Check X-API-Key header (not Authorization: Bearer)
{
"error" : "Profile not found" ,
"details" : "No profile found with the provided credentials for this partner"
}
Solution:
Ensure organization setup was called first
Verify the internal_user_id matches what was used in setup
Check you’re using the correct partner API key
{
"error" : "Profile not set up for authentication" ,
"details" : "Profile must be linked to an auth user. Please complete setup first."
}
Solution: Contact Sticker support—the profile exists but isn’t properly linked.
Best Practices
Create tokens only when the user clicks to open supplies. Don’t pre-generate or cache tokens.
Always call the handshake from your backend. Never expose your API key in frontend code. ✅ Frontend → Your Backend → Sticker API
❌ Frontend → Sticker API directly
If a user takes too long to load the page (>5 min), generate a fresh token: // Track when token was generated
const tokenGeneratedAt = Date . now ();
// On iframe error, check if token might be expired
if ( Date . now () - tokenGeneratedAt > 4 * 60 * 1000 ) {
// Token is probably expired, get a new one
await reauthenticate ();
}
If the iframe fails to load, show a retry button that generates a new token: if ( error ) {
return (
< div >
< p > Failed to load supplies </ p >
< button onClick = { authenticate } > Retry </ button >
</ div >
);
}
Testing
Test the handshake flow:
Setup a test user via organization setup endpoint
Call handshake with the test user’s internal_user_id
Verify response contains valid iframe_embed_url
Open the URL in a browser to confirm authentication works
# Test the handshake
curl -X POST https://api.usesticker.com/v1/partner/handshake \
-H "X-API-Key: sk_test_your_key" \
-H "Content-Type: application/json" \
-d '{"internal_user_id": "test-user-123"}'
Next Steps