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:

  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;
}

403 Forbidden — insufficient scope

Problem: Token is valid but lacks required permissions.

Solution:

  1. Check the endpoint's required scopes in the API docs (x-required-scopes field on each operation)
  2. Mint a new API key in Studio (Settings → API Keys) with the needed scopes — scopes are chosen at key-creation time, not editable afterward
  3. For embed tokens, ensure the intent matches the component you're mounting

Template Issues

Fields not appearing on rendered documents

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

Causes:

  1. includeIf evaluates to false
  2. Field position is outside page bounds (PDF templates)
  3. Missing entity data referenced in expressions

Debugging:

  1. 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
  2. Temporarily remove includeIf 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:
$.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:

  1. Verify the entity was updated via GET /v1/orgs/{orgId}/namespaces/{namespaceKey}/workflows/{id}
  2. Check that onComplete.updates transforms are correct
  3. 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:

  1. includeIf evaluating to false
  2. Incorrect execution mode on group steps
  3. Children not properly nested under the group

Debugging:

  1. Check step includeIf expressions with your test data using signstack preview
  2. Verify group step has execution: sequential or execution: parallel
  3. Confirm all child steps are listed in the children array

onComplete updates not updating entities

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

Common causes:

  1. Transform expression returns undefined instead of an object
  2. Target entity key doesn't match a blueprint input key
  3. validate expression returns false, 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:

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

Solution:

  1. Check that participants array is populated in the blueprint
  2. Verify all required inputs have corresponding entity data
  3. Test root step's includeIf with your entity data using signstack preview

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 includeIf
  • 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 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:

  1. Token has expired
  2. Current page origin isn't in allowedOrigins
  3. Token's intent doesn't match the component you're mounting (e.g. a workflow_editing token 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:

  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. Read errorInfo.message on the 400 response for the specific field problem (see Error Handling)
  2. Verify data types match the schema (strings vs numbers, etc.)
  3. 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:

  1. Typo in the schemaKey or schemaVersion
  2. Schema hasn't been published yet — only a draft version exists and you're pinning to a specific published version (e.g. my_schema@1.0.0) that doesn't yet exist
  3. 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/draft with { "versionType": "minor" } (or "patch" / "major") — this emits 1.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:

  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. 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 signstack command 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.