Create a Basic Template via API

A Template is the detailed blueprint for a single document within SignStack. It defines the base PDF document, the interactive fields overlaid on it, and the participant roles and required data (entity slots). It specifies what the document contains and needs but does not define the workflow steps (that's the job of a Blueprint). This guide shows how to create your first basic Template using the API.

Think of creating a Template like designing a reusable form – you define the background PDF and where the blanks go.

Understanding Field Values: The SignStack Difference (Decoupling via Entities)

Before defining the template, it's crucial to understand how SignStack handles field values differently from many systems. Instead of your application code needing to know specific field keys to push values, fields in SignStack pull their values from the workflow's central Entity data using expressions.

  1. Work with Your Business Objects (Entities): SignStack revolves around the canonical Entities that represent your core business data (like clientInfo, dealInfo). These align directly with the business objects you already use in your own backend. Your application code primarily interacts with these familiar structures.

  2. Declare Data Needs (entitySlots): Your Template declares the Entities it needs using the entitySlots array. This defines the "data contract."

    "entitySlots": [
      { "key": "employeeInfo", "schemaKey": "employeeSchemaV1", ... },
      { "key": "offerDetails", "schemaKey": "offerSchemaV1", ... }
    ]
  3. Template Logic Pulls from Entities (valueExpression): Each field on the template uses a JSONata expression (valueExpression) to define how it should calculate its value based on the data available in the EntityContext. The template, not your application code, owns this presentation logic.

    // Template field knows how to get the name from the 'employeeInfo' entity
    "valueExpression": "entities.employeeInfo.firstName & ' ' & entities.employeeInfo.lastName"
  4. Application Code Stays Clean: Your backend application code doesn't need to know about specific field keys like employeeNameField or salaryField. Its only job is to provide the required employeeInfo and offerDetails Entity objects that conform to the specified schema when starting the Workflow.

The Benefit: True Decoupling & Centralized Logic. This approach completely decouples your backend application logic from the presentation details of your templates. The Entities act as the clean interface between your system and SignStack. All presentation logic (valueExpression, displayCondition) lives within the Template, centralizing your document-related rules within SignStack and keeping your application code focused on core business logic. This makes both your application and your templates easier to manage, evolve, and audit.

Prerequisites

Before creating a Template, you first need to upload the base PDF document to SignStack and get its unique fileId.

  1. Upload your Base PDF: Use the File Management API (POST /files/initiate-upload, upload the file) to upload your base document (e.g., Offer_Letter.pdf). Make sure to calculate and include the contentHash.

  2. Store the fileId: Keep the returned fileId handy; you'll need it when defining the template.

(See Core Concepts: File Management for details on file uploads.)

1. Define the Template JSON

Now, construct the full JSON payload for your Template. This includes defining its manifest (roles, entitySlots) and its fields.

Example template_payload.json:

{
  "key": "simpleOfferLetter", // Unique camelCase key for this template family
  "displayName": "Simple Offer Letter Template",
  "version": "1.0.0", // First version is always 1.0.0
  "status": "Draft", // Start as Draft
  "templateFormat": "PDF", // Specify PDF format
  "fileId": "file_abc123xyz...", // The fileId obtained in prerequisites
  "roles": [ // Declare participant roles needed
    { "key": "newHire", "displayName": "New Hire", "roleCategory": "Signer", "isMandatory": true },
    { "key": "hrManager", "displayName": "HR Manager", "roleCategory": "Signer", "isMandatory": true }
  ],
  "entitySlots": [ // Declare data entities needed
    {
      "key": "offerDetails", // Local key/variable name for this entity
      "displayName": "Offer Details",
      "schemaKey": "offerSchemaV1", // Link to the schema definition
      "compatibleSchemaVersion": "^1.0.0",
      "isMandatory": true
    },
    {
      "key": "employeeInfo",
      "displayName": "Employee Info",
      "schemaKey": "employeeSchemaV1",
      "compatibleSchemaVersion": "^1.0.0",
      "isMandatory": true
    }
  ],
  "fields": [ // Define the interactive fields
    {
      "key": "employeeNameDisplay",
      "type": "Text",
      "valueExpression": "entities.employeeInfo.firstName & ' ' & entities.employeeInfo.lastName",
      "widgets": [ /* Position info */ ]
    },
    {
      "key": "employeeEmailReview",
      "type": "Text",
      "assignedToRoleKey": "newHire",
      "valueExpression": "entities.employeeInfo.email", // Gets initial value
      "widgets": [ /* Position info */ ]
    },
    {
      "key": "bonusAmountField", // Example field using displayCondition
      "type": "Text",
      "assignedToRoleKey": "hrManager", // HR enters bonus if applicable
      // Only show this field if the 'baseSalary' in offerDetails is above 120000
      "displayCondition": "entities.offerDetails.baseSalary > 120000",
      "widgets": [ /* Position info */ ]
    },
    {
      "key": "employeeSignature",
      "type": "Signature",
      "assignedToRoleKey": "newHire",
      "widgets": [ /* Position info */ ]
    },
    {
      "key": "hrSignature",
      "type": "Signature",
      "assignedToRoleKey": "hrManager",
      "widgets": [ /* Position info */ ]
    }
  ]
}
  • Manifest: Defines the roles and entitySlots this template requires.

  • fileId: Links to the base PDF you uploaded.

  • fields: Defines each interactive element.

    • assignedToRoleKey is optional (usually used for interactive fields).

    • valueExpression defines how the field calculates its initial value from Entities.

    • displayCondition is an optional JSONata expression that controls field visibility. It's evaluated against the EntityContext – if true, the field is shown; otherwise, it's hidden.

    • widgets define the field's position and appearance.

(Note: Refer to the API Reference for the full schema details.)

2. Create the Template via API

Save the JSON structure above into a file (e.g., template_payload.json) and then create the Template using the API.

  • Endpoint: POST /v1/organizations/{organizationId}/templates

  • Authentication: Requires a valid SignStack JWT Access Token.

Example curl Request:

curl -X POST \
  [https://api.signstack.com/v1/organizations/me/templates](https://api.signstack.com/v1/organizations/me/templates) \
  -H "Authorization: Bearer <YOUR_ACCESS_TOKEN>" \
  -H "Content-Type: application/json" \
  --data @template_payload.json

3. Success Response

If successful, the API will return a 201 Created status and the full object for the newly created Template version (v1.0.0 with status: "Draft").

Example Response (truncated):

{
  "id": "tpl_123abc456def",
  "organizationId": "org_...",
  "key": "simpleOfferLetter",
  "displayName": "Simple Offer Letter Template",
  "version": "1.0.0",
  "status": "Draft",
  // ... other properties ...
  "createdAt": "...",
  "updatedAt": "..."
}

You have now created your first Template via the API, defining its structure and data-driven logic! This template can now be referenced by its key (simpleOfferLetter) when designing Blueprints or creating Workflows. Remember to update its status from Draft to Active when it's ready for use.

➡️ Next: Guides: Making Fields Dynamic with Expressions