JSON-e Templates Guide for Sorcha Blueprints
Table of Contents
- Introduction
- Basic Syntax
- Operators Reference
- Blueprint Template Patterns
- Best Practices
- Complete Examples
- Troubleshooting
Introduction
JSON-e is a templating language that allows you to generate JSON dynamically. In Sorcha, JSON-e templates enable you to create reusable, parameterized blueprint definitions.
Why Use Templates?
✓ Reusability - Single template generates multiple blueprint variants ✓ Maintainability - Update template once, propagate to all instances ✓ Policy-Driven - Business rules determine workflow structure ✓ Environment-Specific - Different configs for dev/staging/prod ✓ Reduced Errors - Consistent structure across similar workflows
When to Use Templates
Use templates when:
- Creating multiple similar blueprints (e.g., different loan types)
- Supporting multi-environment deployments
- Implementing policy-driven workflows
- Testing different workflow configurations
Don't use templates for:
- One-off unique workflows
- Simple blueprints that rarely change
- When team is unfamiliar with templating
Basic Syntax
Variable Substitution
Replace a value with a variable from context:
{
"name": { "$eval": "userName" }
}Context:
{
"userName": "John Doe"
}Result:
{
"name": "John Doe"
}Conditional Rendering
Include/exclude sections based on conditions:
{
"$if": "includeField",
"then": { "field": "value" },
"else": { "field": "default" }
}Context:
{
"includeField": true
}Result:
{
"field": "value"
}Iteration
Generate multiple items from an array:
{
"items": {
"$map": { "$eval": "products" },
"each(product)": {
"name": { "$eval": "product.name" },
"price": { "$eval": "product.price" }
}
}
}Context:
{
"products": [
{ "name": "Widget", "price": 9.99 },
{ "name": "Gadget", "price": 19.99 }
]
}Result:
{
"items": [
{ "name": "Widget", "price": 9.99 },
{ "name": "Gadget", "price": 19.99 }
]
}Operators Reference
$eval
Evaluate an expression from context:
{ "$eval": "variableName" }Can evaluate complex expressions:
{ "$eval": "basePrice * quantity" }$if / $then / $else
Conditional rendering:
{
"$if": "condition",
"then": valueIfTrue,
"else": valueIfFalse
}Example:
{
"approver": {
"$if": "requiresSeniorApproval",
"then": "senior-officer",
"else": "loan-officer"
}
}$map
Iterate over an array:
{
"$map": { "$eval": "arrayVariable" },
"each(item)": {
"value": { "$eval": "item.field" }
}
}With index:
{
"$map": { "$eval": "items" },
"each(item, index)": {
"id": { "$eval": "index" },
"name": { "$eval": "item.name" }
}
}$flattenDeep
Flatten nested arrays:
{
"$flattenDeep": [
[1, 2],
[[3, 4], 5],
6
]
}Result: [1, 2, 3, 4, 5, 6]
$merge
Merge multiple objects:
{
"$merge": [
{ "a": 1 },
{ "b": 2 },
{ "c": 3 }
]
}Result: { "a": 1, "b": 2, "c": 3 }
$let
Define local variables:
{
"$let": {
"doubled": { "$eval": "value * 2" }
},
"in": {
"original": { "$eval": "value" },
"doubled": { "$eval": "doubled" }
}
}$switch
Pattern matching (like switch/case):
{
"$switch": {
"category": {
"$eval": "productType"
},
"electronics": { "taxRate": 0.08 },
"food": { "taxRate": 0.02 },
"default": { "taxRate": 0.05 }
}
}Blueprint Template Patterns
Parameterized Participants
Add participants conditionally:
{
"participants": [
{
"id": "applicant",
"name": "Applicant"
},
{
"id": "reviewer",
"name": "Reviewer"
},
{
"$if": "requiresSeniorApproval",
"then": {
"id": "senior-reviewer",
"name": "Senior Reviewer"
}
}
]
}Note: JSON-e will remove null values, so when condition is false, the participant won't appear.
Dynamic Action Count
Generate variable numbers of actions:
{
"actions": {
"$flattenDeep": [
[
{
"id": 0,
"title": "Submit Request",
"sender": "requester"
}
],
{
"$if": "requiresManagerApproval",
"then": [
{
"id": 1,
"title": "Manager Approval",
"sender": "manager"
}
],
"else": []
},
{
"$if": "requiresFinanceReview",
"then": [
{
"id": 2,
"title": "Finance Review",
"sender": "finance"
}
],
"else": []
},
[
{
"id": 3,
"title": "Complete",
"sender": "system"
}
]
]
}
}Environment-Specific Configuration
Different settings per environment:
{
"participants": [
{
"id": "approver",
"name": "Approver",
"organisation": {
"$eval": "environment == 'production' ? 'Real Bank Inc' : 'Test Bank LLC'"
},
"walletAddress": {
"$eval": "environment == 'production' ? prodWallet : testWallet"
}
}
]
}Context:
{
"environment": "development",
"prodWallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"testWallet": "0x0000000000000000000000000000000000000000"
}Nested Context Pattern
For complex templates, use nested context:
{
"$eval": "blueprintTemplate",
"context": {
"blueprintTemplate": {
"id": { "$eval": "blueprintId" },
"title": { "$eval": "blueprintTitle" },
"participants": { "$eval": "participants" }
},
"blueprintId": "my-blueprint-001",
"blueprintTitle": "My Blueprint",
"participants": [...]
}
}This pattern:
- Keeps template definition separate
- Makes context variables explicit
- Easier to debug and test
Best Practices
1. Use Nested Context for Complex Templates
✓ Good:
{
"$eval": "template",
"context": {
"template": { ... },
"param1": "value1",
"param2": "value2"
}
}✗ Bad:
{
"field1": { "$eval": "param1" },
"field2": {
"nested": {
"field3": { "$eval": "param2" }
}
}
}2. Provide Sensible Defaults
Always include default parameters:
{
"defaultParameters": {
"minAmount": 1000,
"maxAmount": 100000,
"requiresApproval": true
}
}3. Validate Parameters
Define a JSON Schema for parameters:
{
"parameterSchema": {
"type": "object",
"properties": {
"blueprintId": {
"type": "string",
"minLength": 3
},
"minAmount": {
"type": "number",
"minimum": 0
}
},
"required": ["blueprintId"]
}
}4. Document with Examples
Provide 2-3 concrete examples:
{
"examples": [
{
"name": "standard",
"description": "Standard configuration",
"parameters": { ... }
},
{
"name": "high-value",
"description": "For high-value transactions",
"parameters": { ... }
}
]
}5. Keep Templates Readable
✗ Bad - Deeply nested and hard to read:
{
"$if": "a",
"then": {
"$if": "b",
"then": {
"$if": "c",
"then": "value"
}
}
}✓ Good - Use $let for clarity:
{
"$let": {
"shouldInclude": { "$eval": "a && b && c" }
},
"in": {
"$if": "shouldInclude",
"then": "value"
}
}6. Test All Examples
Always verify that examples evaluate correctly:
GET /api/templates/{id}/examples/{exampleName}Complete Examples
Simple Loan Application Template
{
"id": "loan-app-simple",
"title": "Simple Loan Application",
"template": {
"$eval": "blueprint",
"context": {
"blueprint": {
"id": { "$eval": "blueprintId" },
"title": { "$eval": "title" },
"description": "Loan application workflow",
"version": 1,
"participants": [
{
"id": "applicant",
"name": "Applicant"
},
{
"id": "officer",
"name": { "$eval": "officerTitle" },
"organisation": { "$eval": "bankName" }
}
],
"actions": [
{
"id": 0,
"title": "Submit Application",
"sender": "applicant",
"dataSchemas": [
{
"type": "object",
"properties": {
"amount": {
"type": "number",
"minimum": { "$eval": "minAmount" },
"maximum": { "$eval": "maxAmount" }
},
"purpose": {
"type": "string"
}
},
"required": ["amount", "purpose"]
}
]
},
{
"id": 1,
"title": "Review",
"sender": "officer",
"dataSchemas": [
{
"type": "object",
"properties": {
"decision": {
"type": "string",
"enum": ["approved", "rejected"]
}
}
}
]
}
]
},
"blueprintId": { "$eval": "blueprintId" },
"title": { "$eval": "title" },
"officerTitle": { "$eval": "officerTitle" },
"bankName": { "$eval": "bankName" },
"minAmount": { "$eval": "minAmount" },
"maxAmount": { "$eval": "maxAmount" }
}
},
"parameterSchema": {
"type": "object",
"properties": {
"blueprintId": { "type": "string" },
"title": { "type": "string" },
"bankName": { "type": "string" },
"officerTitle": { "type": "string" },
"minAmount": { "type": "number" },
"maxAmount": { "type": "number" }
},
"required": ["blueprintId", "title", "bankName"]
},
"defaultParameters": {
"blueprintId": "loan-001",
"title": "Loan Application",
"bankName": "Community Bank",
"officerTitle": "Loan Officer",
"minAmount": 1000,
"maxAmount": 100000
}
}Multi-Environment Template
{
"id": "env-aware-workflow",
"template": {
"$eval": "blueprint",
"context": {
"blueprint": {
"id": { "$eval": "blueprintId" },
"participants": [
{
"id": "approver",
"walletAddress": {
"$switch": {
"env": { "$eval": "environment" },
"production": { "$eval": "prodWallet" },
"staging": { "$eval": "stagingWallet" },
"default": { "$eval": "devWallet" }
}
}
}
],
"actions": [
{
"id": 0,
"calculations": {
"fee": {
"$if": "environment == 'production'",
"then": { "$eval": "amount * 0.02" },
"else": 0
}
}
}
]
},
"blueprintId": { "$eval": "blueprintId" },
"environment": { "$eval": "environment" },
"prodWallet": { "$eval": "prodWallet" },
"stagingWallet": { "$eval": "stagingWallet" },
"devWallet": { "$eval": "devWallet" },
"amount": { "$eval": "amount" }
}
},
"defaultParameters": {
"environment": "development",
"prodWallet": "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb",
"stagingWallet": "0x1111111111111111111111111111111111111111",
"devWallet": "0x0000000000000000000000000000000000000000"
}
}Troubleshooting
Template Evaluation Fails
Problem: Template doesn't evaluate
Solutions:
- Check JSON syntax (commas, brackets, quotes)
- Verify all
$evalvariables exist in context - Test with simple template first
- Enable trace mode:
includeTrace: true
Generated Blueprint is Invalid
Problem: Evaluation succeeds but blueprint fails validation
Solutions:
- Enable validation:
validate: true - Check required fields (title, description, participants, actions)
- Verify participant references in actions
- Test with minimal template
Parameters Not Applied
Problem: Template uses default values instead of provided parameters
Solutions:
- Verify parameter names match template
$evalexpressions - Check context nesting
- Ensure parameters are in correct format (string vs number)
- Use trace mode to see context values
Conditional Not Working
Problem: $if always evaluates to same result
Solutions:
- Check boolean logic in condition
- Verify variable names
- Remember:
$ifexpects truthy/falsy values - Use explicit comparisons:
environment == 'production'
Array Generation Issues
Problem: Extra nulls or missing items in arrays
Solutions:
- Use
$flattenDeepto remove nulls - Return empty array
[]in else clause - Filter nulls after generation