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:

  1. Check that you're using the accessToken value, not the full response object
  2. Ensure the Authorization header format is Bearer <token> (note the space)
  3. Verify the token hasn't expired (check expiresAt in 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:

  1. Check the endpoint's required scopes in the API docs (x-required-scopes field)
  2. Create a new API key with the needed scopes in your dashboard
  3. For embed tokens, ensure the intent matches your use case

Template Issues

Fields not appearing on rendered documents

Problem: Template fields aren't visible in the final PDF.

Causes:

  1. displayCondition evaluates to false
  2. Field position is outside page bounds
  3. Missing entity data referenced in expressions

Debugging:

  1. Test with a Scenario that has all required data
  2. Temporarily remove displayCondition to verify field placement
  3. 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:

  1. Verify the entity was updated via GET /v1/orgs/{orgId}/workflows/{id}
  2. Check that onCompleteMappers syntax is correct
  3. 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:

  1. inclusionCondition evaluating to false
  2. Incorrect executionMode on container steps
  3. Missing or incorrect childStepKeys configuration

Debugging:

  1. Check step inclusionCondition expressions with your test data
  2. Verify container step has executionMode: "sequential" or "parallel"
  3. Confirm all child step keys are listed in childStepKeys array

onCompleteMappers not updating entities

Problem: Entity data doesn't change after step completion.

Common causes:

  1. Expression returns undefined instead of the entity object
  2. Field path in expression doesn't match actual document/field keys
  3. 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:

  1. No participants assigned
  2. Missing required entities
  3. Root step's inclusionCondition is false

Solution:

  1. Check that participants array is populated
  2. Verify all required entitySlots have corresponding entities
  3. Test root step's inclusionCondition with your entity data

Workflow Issues

Problem: Participant was assigned but didn't get an email.

Causes:

  1. Workflow hasn't reached that participant's step yet
  2. Email is in spam/junk folder
  3. Email was suppressed due to bounces

Solution:

  1. Check workflow status - participant steps only trigger when reached
  2. Verify participant email is correct via API
  3. 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 iterator and inclusionCondition
  • If changes are needed, cancel and create a new workflow

Webhook not receiving events

Problem: Configured webhook isn't being called.

Debugging checklist:

  1. Verify endpoint URL is publicly accessible (not localhost)
  2. Check endpoint returns 2xx status code
  3. Ensure SSL certificate is valid (self-signed certs won't work)
  4. 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:

  1. Token has expired
  2. Current domain not in allowedOrigins
  3. 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:

  1. Add your domain to allowedOrigins when creating the embed token
  2. Include both http and https variants if testing locally
  3. 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:

  1. Check the error details for specific field issues
  2. Verify data types match schema (strings vs numbers)
  3. 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:

  1. Schema is in draft status (must be active)
  2. Typo in schema key
  3. Schema belongs to different organization

Solution:

  1. Activate the schema: PATCH /v1/orgs/{orgId}/schemas/{key} with {"status": "active"}
  2. 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:

  1. Reduce PDF file size (compress images)
  2. Simplify complex JSONata expressions
  3. Minimize number of fields per page
  4. Use Custom Functions for repeated logic instead of duplicating expressions

Rate limiting during bulk operations

Problem: Getting 429 errors when creating many resources.

Solutions:

  1. Implement exponential backoff with jitter
  2. Batch operations where possible
  3. 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:

  1. Map your document templates to SignStack Templates
  2. Convert your workflow logic to Blueprints
  3. Update API integrations to use SignStack endpoints
  4. Run parallel tests before full cutover