Templates
The restricted {{ }} expression language — scopes, safe access, and why it isn't just eval.
Siphon interpolates values with a restricted {{ }} template language. It is
deliberately not a general expression evaluator: no eval, no function calls,
no operators. It supports property access, optional chaining, and indexing —
enough to thread request data through, and nothing that could execute arbitrary
code against a tenant's secrets.
path: /api/Deals/{{ record.id }}
query: { from: "{{ watermark }}" }
url: "{{ params.baseUrl }}/api/Auth/login"
format: "Bearer {{ token }}"Scopes
A template resolves against a fixed set of scopes, depending on where it appears:
| Scope | Available in | Holds |
|---|---|---|
params | anywhere | non-secret connection params |
secrets | auth only | encrypted connection secrets |
record | enrich, transform | the current record (+ enriched fields) |
page | pagination | page/cursor state |
watermark | fetch | the incremental cursor |
run | anywhere | run metadata (e.g. run.id) |
env | anywhere | injected environment values |
token | auth.inject | the freshly exchanged token |
Secrets are sandboxed
{{ secrets.* }} resolves only inside auth. You cannot reference a secret
from a fetch URL, a header, or a transform — so credentials can't leak into logs
or delivered data.
Supported syntax
- Property access:
record.id,params.baseUrl - Optional chaining:
record._detail?.clientId - Indexing:
record.items[0],record['odd-key'] - Nested across enrich hops:
record._detail.clientId
Blocked keys (__proto__, prototype, constructor) are rejected to prevent
prototype-pollution.
Missing values
By default a missing path throws (so a typo fails loudly at author time). Some
contexts use an empty or null policy instead — e.g. an optional query param
resolves to empty rather than erroring.
JSONata vs templates
Two different tools, two different jobs:
{{ }}templates thread individual values into requests (URLs, headers, query, token exchange bodies). Restricted, safe, no logic.- JSONata reshapes a whole record in the transform stage. Full query/transform language, sandboxed to the record.