Templates

In the previous article, we covered the "data-first" principle and how Entities provide the authoritative data for your workflow. Now, let's explore Templates—the "view" layer that makes this data visible and interactive on your documents.

A Template is a reusable blueprint for a single document. It defines the underlying PDF, the interactive fields placed on it, and the data it requires. If an Entity is the raw data, the Template is the form that gives it structure and meaning within a document context.

1. The "Manifest": Declaring Requirements

A Template clearly declares its dependencies—the participants and data it needs to function correctly.

  • roles: This is the "cast list." It defines the participant placeholders this document requires, each with a unique key (e.g., primary_buyer, seller_agent).
  • entitySlots: This is the "data list." It defines the Entity data this document needs, referencing specific Schemas by their key (e.g., client_info, deal_info).

2. The Base Document: PDF

SignStack Templates work by overlaying interactive elements onto a base PDF document. You upload your existing PDF (like a standard real estate form or a company contract), and that becomes the static background for your template.

3. The "Logic": Fields and Expressions

Fields are the interactive elements placed onto the PDF. They connect your data to your participants. Each field definition includes its key, type (e.g., Signature, Text), and visual properties (widgets defining position, size, etc.).

Most importantly, Fields use JSONata expressions to create powerful, data-driven logic:

  • assignedToRoleKey: Assigns a field to a specific Role defined in the template (e.g., assigning "buyer_signature_field" to the "primary_buyer" role key).
  • valueExpression: This expression calculates the field's value "just-in-time" by executing logic using data from the EntityContext.
    • Simple Example (1-to-1 Mapping): Display the client's name:
      client_info.name
    • Advanced Example (Combining Data): Calculate a fee based on deal size and client status:
      "$formatNumber(client_info.tier = 'Premium' and deal_info.value > 50000 ? deal_info.value * 0.02 : 0, '$,0.00')"
  • displayCondition: This expression controls visibility by executing logic using data from the EntityContext. If it returns true, the field is shown; otherwise, it's hidden.
    deal_info.requestedDiscount > userInfo.discountLimit

💡 Tip: Need help writing a complex expression? Modern Large Language Models (LLMs) are excellent at generating JSONata expressions!

4. Previewing with Scenarios

How do you check if your dynamic valueExpressions and displayConditions are working correctly while you're designing your Template? SignStack provides Scenarios for this.

A Scenario is a reusable set of sample Entity data. Within the Template editor, you can select a Scenario, and the document preview will instantly update, evaluating all your expressions using that specific data set.

This allows for rapid iteration and testing, ensuring your dynamic fields behave as expected before you even build a Blueprint.

We'll cover Scenarios in more detail in their own Core Concepts article.

5. Outputs: Extracting Data from Completed Steps

While Fields control what participants see and interact with, Outputs define what data should be extracted after a participant completes their signing task. Outputs are JSONata expressions that can access both entity data and the field values entered by the participant, transforming them into structured results for subsequent workflow steps.

Structure:

interface Output {
  key: string; // Unique identifier (e.g., "approvalSummary")
  roleKey: string; // Which role this output belongs to
  schemaKey: string; // Schema that defines the output structure
  schemaVersion: string; // Version of the schema
  expression: string; // JSONata expression to evaluate
}

Example:

{
  "outputs": [
    {
      "key": "client_summary",
      "roleKey": "primary_buyer",
      "schemaKey": "clientSummarySchema",
      "schemaVersion": "1.0.0",
      "expression": "{ 'name': client_info.name, 'phone': fields.phone_number_field, 'signedAt': $now() }"
    }
  ]
}

Output Expression Context

Output expressions have access to:

  • Entity data - Access entities directly by key (e.g., client_info.name, deal_info.value)
  • Field values - Access resolved field values via fields.fieldKey (e.g., fields.phoneNumberField). This includes:
    • Values entered by the participant (overrides)
    • Original field values if no participant input (literals or evaluated expressions)

Why Use Outputs?

  • Separation of Concerns: Fields handle presentation and input; Outputs handle data extraction and transformation.
  • Role-Specific Results: Each output is tied to a specific role, so you can define different data extractions for different participants.
  • Workflow Integration: Outputs are automatically available in onCompleteMappers via the docOutputs context (e.g., docOutputs.documentKey.outputKey), allowing you to evolve your entities based on computed results.

Outputs vs. Fields

Aspect Fields Outputs
Purpose Display data, collect input Extract and transform data
When evaluated During document rendering After step completion
Visible to participant Yes No (internal)
Used in mappers No Yes, via docOutputs.docKey.outputKey

💡 Tip: Use Outputs when you need to compute derived values (totals, summaries, status flags) that should flow into subsequent workflow steps without cluttering your field definitions.

6. Centralized Logic

By defining this logic inside the Template, you are centralizing your business rules within SignStack. Your application code stays clean—it just provides the Entities, and SignStack handles the complex presentation and field-level logic automatically.