Troubleshooting & FAQ
Common issues and frequently asked questions when integrating with SignStack.
Authentication Issues
"invalid_token" error after successful token exchange
Problem: You exchanged your API key for a token, but API calls return 401.
Solutions:
- Check that you're using the
accessTokenvalue, not the full response object - Ensure the Authorization header format is
Bearer <token>(note the space) - Verify the token hasn't expired (check
expiresAtin the response)
# Correct
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
# Incorrect
Authorization: Bearer {"accessToken": "eyJ..."}
Authorization: eyJhbGciOiJIUzI1NiIs...
Authorization: bearer eyJhbGciOiJIUzI1NiIs...
Token expires too quickly
Problem: Tokens expire after ~1 hour, causing frequent re-authentication.
Solution: Cache and reuse tokens until near expiration:
let cachedToken: { accessToken: string; expiresAt: Date } | null = null;
async function getAccessToken(): Promise<string> {
// Refresh if expired or expiring within 5 minutes
const bufferMs = 5 * 60 * 1000;
if (!cachedToken || Date.now() > cachedToken.expiresAt.getTime() - bufferMs) {
const response = await exchangeApiKey();
cachedToken = {
accessToken: response.accessToken,
expiresAt: new Date(response.expiresAt),
};
}
return cachedToken.accessToken;
}
"insufficient_scope" error
Problem: Token is valid but lacks required permissions.
Solution:
- Check the endpoint's required scopes in the API docs (
x-required-scopesfield) - Create a new API key with the needed scopes in your dashboard
- For embed tokens, ensure the
intentmatches your use case
Template Issues
Fields not appearing on rendered documents
Problem: Template fields aren't visible in the final PDF.
Causes:
displayConditionevaluates tofalse- Field position is outside page bounds
- Missing entity data referenced in expressions
Debugging:
- Test with a Scenario that has all required data
- Temporarily remove
displayConditionto verify field placement - Check the expression in the JSONata exerciser: https://try.jsonata.org/
Expression errors: "Cannot read property of undefined"
Problem: JSONata expression fails because entity data is missing.
Solution: Use defensive expressions:
// Instead of:
entities.client_info.address.city;
// Use:
$exists(entities.client_info.address) ? entities.client_info.address.city : '';
// Or for optional nested properties:
entities.client_info.address.city ? entities.client_info.address.city : 'N/A';
valueExpression not updating
Problem: Field shows stale data despite entity changes.
Cause: The expression might be caching or the entity update didn't persist.
Solution:
- Verify the entity was updated via
GET /v1/orgs/{orgId}/workflows/{id} - Check that
onCompleteMapperssyntax is correct - Ensure the entity key in the expression matches the slot key
Blueprint Issues
Steps not executing in expected order
Problem: Workflow steps run in wrong order or skip unexpectedly.
Causes:
inclusionConditionevaluating tofalse- Incorrect
executionModeon container steps - Missing or incorrect
childStepKeysconfiguration
Debugging:
- Check step
inclusionConditionexpressions with your test data - Verify container step has
executionMode: "sequential"or"parallel" - Confirm all child step keys are listed in
childStepKeysarray
onCompleteMappers not updating entities
Problem: Entity data doesn't change after step completion.
Common causes:
- Expression returns
undefinedinstead of the entity object - Field path in expression doesn't match actual document/field keys
- Using wrong document key (check blueprint's document definitions)
Debugging:
// Log the fields structure to understand the path:
// In onCompleteValidationExpression:
$string(fields);
// Ensure you're using $merge to preserve existing data:
$merge([entities.client_info, { name: fields.docKey.fieldKey.value.text }]);
Workflow stuck in "pending" status
Problem: Workflow was created but never starts.
Causes:
- No participants assigned
- Missing required entities
- Root step's
inclusionConditionisfalse
Solution:
- Check that
participantsarray is populated - Verify all required
entitySlotshave corresponding entities - Test root step's
inclusionConditionwith your entity data
Workflow Issues
Participant not receiving signing link
Problem: Participant was assigned but didn't get an email.
Causes:
- Workflow hasn't reached that participant's step yet
- Email is in spam/junk folder
- Email was suppressed due to bounces
Solution:
- Check workflow status - participant steps only trigger when reached
- Verify participant email is correct via API
- Contact support if emails are being blocked
Can't add documents to an in-progress workflow
Problem: API returns error when adding documents after workflow starts.
Cause: Workflows are immutable once started for audit trail integrity.
Solution:
- Add all documents before calling the start endpoint
- For dynamic documents, use Blueprint's
iteratorandinclusionCondition - If changes are needed, cancel and create a new workflow
Webhook not receiving events
Problem: Configured webhook isn't being called.
Debugging checklist:
- Verify endpoint URL is publicly accessible (not localhost)
- Check endpoint returns 2xx status code
- Ensure SSL certificate is valid (self-signed certs won't work)
- Review delivery logs in the dashboard or via API
# Check recent delivery attempts
GET /v1/orgs/{orgId}/webhooks/{id}/deliveries
Embedded Components
"Token rejected" error in embedded signing
Problem: Embed token is rejected when initializing the component.
Causes:
- Token has expired
- Current domain not in
allowedOrigins - Token was created for different intent or workflow
Solution:
// Ensure origin matches token's allowedOrigins
const embedToken = await createEmbedToken({
intent: 'signing_session',
workflowId: 'wf_xxx',
stepKey: 'signer1',
allowedOrigins: [
window.location.origin, // Include current domain
'https://staging.yourapp.com', // And staging if needed
],
});
CORS errors when loading embedded component
Problem: Browser console shows CORS errors.
Solutions:
- Add your domain to
allowedOriginswhen creating the embed token - Include both
httpandhttpsvariants if testing locally - Don't include trailing slashes in origin URLs
// Correct
allowedOrigins: ['https://yourapp.com', 'http://localhost:3000'];
// Incorrect
allowedOrigins: ['https://yourapp.com/', 'localhost:3000'];
Schema & Entity Issues
Schema validation failing on entity creation
Problem: Adding entities fails with validation errors.
Debugging:
- Check the error
detailsfor specific field issues - Verify data types match schema (strings vs numbers)
- Test your JSON against the schema using a validator
// Common type mismatches:
{
"price": "100" // Wrong: string instead of number
"price": 100 // Correct: number
"isActive": "true" // Wrong: string instead of boolean
"isActive": true // Correct: boolean
}
"Schema not found" when creating resources
Problem: Template or Blueprint creation fails with schema reference error.
Causes:
- Schema is in
draftstatus (must beactive) - Typo in schema key
- Schema belongs to different organization
Solution:
- Activate the schema:
PATCH /v1/orgs/{orgId}/schemas/{key}with{"status": "active"} - List available schemas:
GET /v1/orgs/{orgId}/schemas?status=active
Performance Issues
Slow document rendering
Problem: Template preview or workflow documents take too long to render.
Optimizations:
- Reduce PDF file size (compress images)
- Simplify complex JSONata expressions
- Minimize number of fields per page
- Use Custom Functions for repeated logic instead of duplicating expressions
Rate limiting during bulk operations
Problem: Getting 429 errors when creating many resources.
Solutions:
- Implement exponential backoff with jitter
- Batch operations where possible
- Spread requests over time
async function bulkCreate<T>(items: T[], createFn: (item: T) => Promise<any>) {
const results = [];
for (const item of items) {
try {
results.push(await createFn(item));
} catch (error) {
if (error.status === 429) {
await sleep(parseInt(error.headers['Retry-After'] || '60') * 1000);
results.push(await createFn(item)); // Retry once
} else {
throw error;
}
}
await sleep(100); // Small delay between requests
}
return results;
}
FAQ
Can I use SignStack in test/sandbox mode?
Yes. API keys have an environment property: test or live. Test environment data is isolated and can be freely deleted. Create separate API keys for each environment.
How do I handle document versioning?
Templates and Blueprints use semantic versioning. When you activate a resource, you can specify versionType:
patch: Bug fixes (1.0.0 → 1.0.1)minor: New features, backward compatible (1.0.0 → 1.1.0)major: Breaking changes (1.0.0 → 2.0.0)
Draft versions are editable; active versions are immutable.
Can participants sign on mobile devices?
Yes. Embedded signing components are responsive and work on mobile browsers. Ensure your container allows proper sizing.
How long are completed documents stored?
Completed workflow documents are retained according to your plan's retention policy. Enterprise plans offer configurable retention. Contact support for specific retention requirements.
Can I customize email notifications?
Yes. Configure email templates via the dashboard or API. You can customize subject lines, body content, and branding. Use entity data in templates with the same JSONata expressions used in documents.
Is there an SDK available?
Currently, SignStack provides a REST API. SDKs for popular languages are on the roadmap. The API follows REST conventions and works with any HTTP client.
How do I migrate from another e-signature provider?
Contact our solutions team for migration assistance. Key steps:
- Map your document templates to SignStack Templates
- Convert your workflow logic to Blueprints
- Update API integrations to use SignStack endpoints
- Run parallel tests before full cutover