Troubleshooting & FAQ
Common issues and frequently asked questions when integrating with SignStack.
Authentication Issues
401 Unauthorized 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;
}
403 Forbidden — insufficient scope
Problem: Token is valid but lacks required permissions.
Solution:
- Check the endpoint's required scopes in the API docs (
x-required-scopesfield on each operation) - Mint a new API key in Studio (Settings → API Keys) with the needed scopes — scopes are chosen at key-creation time, not editable afterward
- For embed tokens, ensure the
intentmatches the component you're mounting
Template Issues
Fields not appearing on rendered documents
Problem: Template fields aren't visible in the final PDF.
Causes:
includeIfevaluates tofalse- Field position is outside page bounds (PDF templates)
- Missing entity data referenced in expressions
Debugging:
- Preview the template with a scenario to see how it renders against real data — either from the Preview pane in Studio (available on both template and blueprint editors), or via the CLI:
signstack preview my_template@1.0.0 -s test_data -d ./my-project - Temporarily remove
includeIfto 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:
$.client_info.address.city
// Use:
$exists($.client_info.address) ? $.client_info.address.city : ''
// Or for optional nested properties:
$.client_info.address.city ? $.client_info.address.city : 'N/A'
Field value 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}/namespaces/{namespaceKey}/workflows/{id} - Check that
onComplete.updatestransforms are correct - Ensure the entity key in the expression matches the input key
Blueprint Issues
Steps not executing in expected order
Problem: Workflow steps run in wrong order or skip unexpectedly.
Causes:
includeIfevaluating tofalse- Incorrect
executionmode on group steps - Children not properly nested under the group
Debugging:
- Check step
includeIfexpressions with your test data usingsignstack preview - Verify group step has
execution: sequentialorexecution: parallel - Confirm all child steps are listed in the
childrenarray
onComplete updates not updating entities
Problem: Entity data doesn't change after step completion.
Common causes:
- Transform expression returns
undefinedinstead of an object - Target entity
keydoesn't match a blueprint input key validateexpression returnsfalse, preventing the update
Debugging:
Remember the onComplete transform's return value replaces the target entity wholesale — there's no automatic merge. To preserve existing fields, wrap your patch in $merge([input.<key>, {...}]). Read the signed payload via docOutputs.<doc>.<role>:
# Wrong — wipes out every other field on client_info
onComplete:
updates:
- key: client_info
transform: |
{
"status": "completed",
"completedAt": $now()
}
# Right — preserves existing fields
onComplete:
updates:
- key: client_info
transform: |
$merge([
input.client_info,
{
"status": "completed",
"completedAt": $now(),
"signedEmail": docOutputs.intake_form.client.email
}
])
See Step onComplete — Expression Context for the full variable catalog.
Workflow stuck in "pending" status
Problem: Workflow was created but never starts.
Causes:
- No participants assigned
- Missing required entities
- Root step's
includeIfisfalse
Solution:
- Check that
participantsarray is populated in the blueprint - Verify all required
inputshave corresponding entity data - Test root step's
includeIfwith your entity data usingsignstack preview
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
iteratorandincludeIf - 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 Studio or via the API
# List recent delivery attempts across all configured webhooks
GET /v1/orgs/{orgId}/namespaces/{namespaceKey}/webhooks/deliveries
# Fetch a specific delivery by id
GET /v1/orgs/{orgId}/namespaces/{namespaceKey}/webhooks/deliveries/{deliveryId}
Embedded Components
"Token rejected" error in embedded signing
Problem: Embed token is rejected when initializing the component.
Causes:
- Token has expired
- Current page origin isn't in
allowedOrigins - Token's
intentdoesn't match the component you're mounting (e.g. aworkflow_editingtoken won't work in<ss-signing-embed>)
Solution: Mint the embed token server-side with the current origin in allowedOrigins:
POST /v1/orgs/{orgId}/namespaces/{namespaceKey}/auth/embed
Authorization: Bearer <ACCESS_TOKEN>
{
"intent": "signing_session",
"workflowId": "b6f3a8d2-1c4e-4b9a-a7f3-2e8c1d5a9b4f",
"stepKey": "signer1",
"allowedOrigins": [
"https://yourapp.com",
"https://staging.yourapp.com"
]
}
See Embed SignStack into Your App for the full server + browser walkthrough.
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:
- Read
errorInfo.messageon the 400 response for the specific field problem (see Error Handling) - Verify data types match the schema (strings vs numbers, etc.)
- Test your JSON against the schema's JSON Schema using any standard 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 a schema reference error.
Causes:
- Typo in the
schemaKeyorschemaVersion - Schema hasn't been published yet — only a
draftversion exists and you're pinning to a specific published version (e.g.my_schema@1.0.0) that doesn't yet exist - Schema belongs to a different namespace than your token targets
Solution:
- List available schemas:
GET /v1/orgs/{orgId}/namespaces/{namespaceKey}/schemas - Publish the schema from draft to a concrete semver:
PATCH /v1/orgs/{orgId}/namespaces/{namespaceKey}/schemas/{key}/versions/draftwith{ "versionType": "minor" }(or"patch"/"major") — this emits1.0.0,1.1.0, etc. - Alternatively, reference the draft explicitly from the dependent resource during development:
my_schema@draft
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. Mode is a property of the namespace, not the API key — each namespace is either live or test. API keys inherit their mode from the namespace they're scoped to, so a key minted in a test namespace can only touch test data (and vice versa). Create separate test and live namespaces and mint keys in each. See Namespaces — Test vs. Live Mode.
How do I handle resource versioning?
Templates, Blueprints, Schemas, and other primitives use semantic versioning. When you publish a resource, you specify versionType:
patch: Bug fixes (1.0.0 → 1.0.1)minor: New backward-compatible changes (1.0.0 → 1.1.0)major: Breaking changes (1.0.0 → 2.0.0)
Draft versions are editable and mutate in place; published versions are immutable. To make a further change after publish, edit the draft and publish again to emit the next version.
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.
What integration surfaces are available?
- REST API — the full canonical surface; works with any HTTP client
- CLI — the
signstackcommand for authoring, validating, and pushing primitives from your repo; see Add SignStack to Your CI/CD Pipeline - Embeddable web components —
<ss-signing-embed>,<ss-workflow-editor>,<ss-workflow-monitor>,<ss-resource-editor>for in-app integration; see Embedding Components
How do I validate my YAML resources locally?
Run signstack validate <key>@<version> -d <dir> from your project root. The CLI validates the named resource and its full dependency graph against their schemas and reports any issues. Use --verbose for dependency-graph stats.
signstack validate my_blueprint@draft -d ./agreements
How do I migrate from another e-signature provider?
Email contact@signstack.ai and we'll help scope the migration and pair with you on the rollout.
