Documentation Index Fetch the complete documentation index at: https://embed.usesticker.com/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Follow these best practices to ensure your Sticker integration is secure, performant, and provides the best user experience.
Authentication & Security
DO:
Store API keys in environment variables
Use secrets management systems (AWS Secrets Manager, HashiCorp Vault)
Use different keys for sandbox and production
Keep keys server-side only
DON’T:
Hardcode API keys in source code
Commit keys to version control
Share keys in chat/email
Expose keys in client-side code
DO:
Generate tokens on-demand when user opens module
Handle token expiration gracefully (tokens last 5 min)
Regenerate tokens on authentication errors
Clear any references when user logs out
DON’T:
Pre-generate tokens in advance
Cache or store session tokens
Reuse tokens across sessions
Share tokens between users
DO:
Use sandbox attribute with proper permissions
Implement Content Security Policy
Use HTTPS for all environments
DON’T:
Remove sandbox permissions unnecessarily
Mix HTTP and HTTPS content
Disable security features for convenience
Error Handling
Implement Retry Logic
Use exponential backoff for transient failures:
async function callStickerAPI ( endpoint , options , maxRetries = 3 ) {
let lastError ;
for ( let attempt = 0 ; attempt < maxRetries ; attempt ++ ) {
try {
const response = await fetch ( endpoint , options );
// Don't retry client errors (4xx except 429)
if ( response . status >= 400 && response . status < 500 && response . status !== 429 ) {
const error = await response . json ();
throw new Error ( `Client error: ${ error . message } ` );
}
if ( ! response . ok ) {
throw new Error ( `HTTP ${ response . status } ` );
}
return await response . json ();
} catch ( error ) {
lastError = error ;
// Don't retry on last attempt
if ( attempt === maxRetries - 1 ) break ;
// Exponential backoff: 1s, 2s, 4s
const delay = Math . pow ( 2 , attempt ) * 1000 ;
await new Promise ( resolve => setTimeout ( resolve , delay ));
}
}
throw lastError ;
}
Handle Rate Limiting
Respect rate limits and implement backoff:
async function handleRateLimit ( response , retryFn ) {
if ( response . status === 429 ) {
const data = await response . json ();
const retryAfter = data . retry_after || 60 ;
console . warn ( `Rate limited. Retrying after ${ retryAfter } seconds` );
await new Promise ( resolve =>
setTimeout ( resolve , retryAfter * 1000 )
);
// Retry the request
return retryFn ();
}
return response ;
}
User-Friendly Error Messages
Transform technical errors into helpful messages:
function getUserFriendlyError ( error ) {
const errorMap = {
'VALIDATION_ERROR' : 'Please check your information and try again.' ,
'UNAUTHORIZED' : 'Authentication failed. Please refresh and try again.' ,
'USER_ALREADY_EXISTS' : 'This user is already set up.' ,
'Profile not found' : 'Your account needs to be set up. Please contact your administrator.' ,
'SESSION_EXPIRED' : 'Your session has expired. Refreshing...' ,
'RATE_LIMIT_EXCEEDED' : 'Too many requests. Please wait a moment and try again.' ,
};
return errorMap [ error . code ] || errorMap [ error . message ] ||
'An error occurred. Please try again or contact support.' ;
}
Don’t Cache Session Tokens
Session tokens are single-use and short-lived—never cache them:
// ❌ BAD - Never cache session tokens
const tokenCache = new Map ();
function getCachedToken ( userId ) {
return tokenCache . get ( userId );
}
// ✅ GOOD - Generate fresh each time
async function getSessionToken ( userId ) {
const response = await fetch ( '/api/supplies/auth' , {
method: 'POST' ,
body: JSON . stringify ({ userId })
});
return response . json ();
}
Preconnect to Sticker Domain
Speed up iframe loading:
<!-- Add to your HTML head -->
< link rel = "preconnect" href = "https://shop.usesticker.com" />
< link rel = "dns-prefetch" href = "https://shop.usesticker.com" />
Track integration performance metrics:
class StickerMetrics {
static trackHandshake ( startTime , success ) {
const duration = Date . now () - startTime ;
// Send to your analytics
analytics . track ( 'sticker_handshake' , {
duration_ms: duration ,
success: success
});
}
static trackIframeLoad ( startTime ) {
const duration = Date . now () - startTime ;
analytics . track ( 'sticker_iframe_load' , {
duration_ms: duration
});
}
static trackError ( endpoint , error ) {
analytics . track ( 'sticker_error' , {
endpoint: endpoint ,
error_code: error . code ,
error_message: error . message
});
}
}
// Usage
const start = Date . now ();
try {
const result = await getSessionToken ( userId );
StickerMetrics . trackHandshake ( start , true );
} catch ( error ) {
StickerMetrics . trackHandshake ( start , false );
StickerMetrics . trackError ( 'handshake' , error );
}
User Experience
Loading States
Always show loading indicators:
function SuppliesModule ({ userId }) {
const [ state , setState ] = useState ( 'initializing' );
const [ iframeUrl , setIframeUrl ] = useState ( null );
useEffect (() => {
async function load () {
try {
setState ( 'loading' );
const { iframe_url } = await getSessionToken ( userId );
setIframeUrl ( iframe_url );
setState ( 'ready' );
} catch ( error ) {
setState ( 'error' );
}
}
load ();
}, [ userId ]);
return (
< div className = "supplies-wrapper" >
{ state === 'loading' && (
< div className = "loading-state" >
< Spinner />
< p > Loading supplies... </ p >
</ div >
) }
{ state === 'error' && (
< div className = "error-state" >
< ErrorIcon />
< p > Failed to load supplies </ p >
< button onClick = { () => window . location . reload () } > Try Again </ button >
</ div >
) }
{ iframeUrl && (
< iframe
src = { iframeUrl }
className = "w-full h-full border-0"
style = { { display: state === 'ready' ? 'block' : 'none' } }
onLoad = { () => setState ( 'ready' ) }
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"
/>
) }
</ div >
);
}
Graceful Degradation
Provide fallback options when integration fails:
function SuppliesModuleWithFallback () {
const [ integrationFailed , setIntegrationFailed ] = useState ( false );
if ( integrationFailed ) {
return (
< div className = "fallback-message" >
< h3 > Supplies Module Unavailable </ h3 >
< p > We're experiencing technical difficulties. Please: </ p >
< ul >
< li > Try again in a few minutes </ li >
< li > Contact support: suyash@usesticker.com </ li >
</ ul >
< button onClick = { () => setIntegrationFailed ( false ) } >
Try Again
</ button >
</ div >
);
}
return < StickerEmbed onError = { () => setIntegrationFailed ( true ) } /> ;
}
Data Management
Use Your Internal IDs
Always use your internal identifiers for easy correlation:
// Good - uses your internal IDs
await setupOrganization ({
internalOrgId: organization . id , // Your org ID
internalUserId: user . id , // Your user ID
shippingLocations: locations . map ( loc => ({
internalShippingLocationId: loc . id , // Your location ID
...
}))
});
// Later, for handshake
await handshake ({
internal_user_id: user . id // Same ID you used in setup
});
Handle Existing Users Gracefully
The setup endpoint returns 409 if user already exists—don’t treat this as an error:
async function ensureUserSetup ( org , user ) {
try {
const result = await setupOrganization ( org , user );
if ( result . data . isNewOrganization ) {
console . log ( 'New organization created' );
} else {
console . log ( 'User added to existing organization' );
}
return result . data . profile . id ;
} catch ( error ) {
// 409 means user already exists - that's fine!
if ( error . code === 'USER_ALREADY_EXISTS' ) {
console . log ( 'User already set up:' , error . data . profileId );
return error . data . profileId ;
}
throw error ;
}
}
Testing
Integration Tests
Test your integration thoroughly:
describe ( 'Sticker Integration' , () => {
it ( 'should setup organization successfully' , async () => {
const result = await setupOrganization ( mockOrgData , mockUser , mockLocations );
expect ( result . success ). toBe ( true );
expect ( result . data . profile . id ). toBeDefined ();
expect ( result . data . organization . id ). toBeDefined ();
});
it ( 'should handle handshake and return session token' , async () => {
const result = await getHandshakeToken ( 'test-user-123' );
expect ( result . session_key ). toBeDefined ();
expect ( result . session_key ). toHaveLength ( 64 );
expect ( result . iframe_embed_url ). toContain ( 'session_key' );
});
it ( 'should retry on transient failures' , async () => {
mockAPI . failNextRequests ( 2 ); // Fail twice, succeed third
const result = await callStickerAPI ( endpoint , options );
expect ( result ). toBeDefined ();
expect ( mockAPI . callCount ). toBe ( 3 );
});
it ( 'should return user-friendly error for missing profile' , async () => {
mockAPI . returnError ( 404 , { error: 'Profile not found' });
const error = await callStickerAPI ( endpoint , options ). catch ( e => e );
const friendlyMessage = getUserFriendlyError ( error );
expect ( friendlyMessage ). not . toContain ( '404' );
expect ( friendlyMessage ). toContain ( 'account' );
});
});
Use Sandbox Environment
Always test in sandbox first:
const config = {
staging: {
apiUrl: 'https://api.staging.usesticker.com/v1' ,
apiKey: process . env . STICKER_STAGING_API_KEY
},
production: {
apiUrl: 'https://api.usesticker.com/v1' ,
apiKey: process . env . STICKER_API_KEY
}
};
const env = process . env . NODE_ENV === 'production' ? 'production' : 'staging' ;
const stickerConfig = config [ env ];
Deployment Checklist
Before going live, verify:
Getting Help
Partner Support Technical support for integration issues
Schedule Call Book time with solutions engineers