Skip to main content
Most of the time you don’t have to write template expressions yourself—the agent generates them for you. Still, understanding how they work helps when you’re debugging or tweaking a tool. At their core, template expressions use the <<expression>> pattern, where expression is evaluated at runtime.

Where template expressions are used

The same template engine is used across superglue:
  • Integrations – connection strings and URL templates
  • Step configurationurlHost, urlPath, headers, queryParams, body
  • Transforms – final transforms and data selectors

Template expression modes

There are two kinds of template expressions: simple expressions and arrow function expressions.

Simple expressions (legacy)

Simple expressions resolve values directly from the context (payload, credentials, previous step results) without any JavaScript. Syntax:
  • <<variable>>
  • <<variable.key>> for nested fields
Examples: In the context of a tool step:
headers: {
  "Authorization": "Bearer <<stripe_apiKey>>"
},
queryParams: {
  user_id: "<<userId>>"
}
or in an integration:
postgres://<<username>>:<<password>>@<<host>>:<<port>>/<<database>>
Arrow function expressions use JavaScript arrow functions that receive the context as a single parameter (typically named sourceData) and return the value to insert. superglue runs these functions in a sandboxed JavaScript VM.
<<(sourceData) => {
  // read from sourceData and return the value to insert
}>>
Arrow function expressions are also used in places that are pure code (for example, the finalTransform field), where you don’t wrap them in << >>. They still receive the same sourceData context object and behave the same way.
Examples:
headers: {
  "Authorization": "<<(sourceData) => `Bearer ${sourceData.credentials.apiKey}`>>"
}
body: {
  posts: "<<(sourceData) => {
    const fromJsonPlaceholder =
      (sourceData.fetchJsonPlaceholderPosts ?? []).map(p => ({
        id: p.id,
        title: p.title,
        source: 'jsonplaceholder'
      }));

    const fromDummyJson =
      (sourceData.fetchDummyJsonPosts?.posts ?? []).map(p => ({
        id: p.id,
        title: p.title,
        source: 'dummyjson'
      }));

    return [...fromJsonPlaceholder, ...fromDummyJson];
  }>>"
}

Template context

Template expressions evaluate against a context object that depends on where they’re used (step config, integration, or transform).
  • Arrow function expressions – context is passed as the function parameter (typically sourceData)
  • Simple expressions – the string inside << >> is resolved against the same context object
The context looks different depending on where the expression is used:

Integration context

The context is built from the integration credentials (for example, username, password, access_token). The sourceData might look like this:
{
  "username": "db_user",
  "password": "secret123",
  "host": "db.example.com",
  "port": "5432",
  "database": "production"
}
Integration templates are only resolved at tool execution time and get merged with tool-specific credentials.

Tool step context

The context is the aggregated step input, which includes:
  • payload – workflow input payload
  • credentials – resolved credentials for the integration
  • Previous step results keyed by step ID (for example, getCustomers)
  • currentItem – current iteration data when the step runs in a loop
The sourceData object might look like this:
{
  "payload": { "userId": "12345", "startDate": "2024-01-01" },
  "credentials": { "apiKey": "sk_test_..." },
  "getCustomers": {
    "data": [{ "id": "cus_123", "email": "[email protected]" }]
  }
}

Pagination variables

When pagination is configured on a step, the following variables are available in the request configuration (urlPath, queryParams, headers, body):
VariableDescription
pageCurrent page number (starts at 1)
offsetCurrent offset (starts at 0, increments by pageSize)
cursorCursor value extracted from the previous response
limitSame as pageSize
pageSizeThe configured page size
Examples: Page-based pagination:
queryParams: {
  "page": "<<page>>",
  "per_page": "<<limit>>"
}
Offset-based pagination:
queryParams: {
  "offset": "<<offset>>",
  "limit": "<<limit>>"
}
Cursor-based pagination:
queryParams: {
  "cursor": "<<cursor>>",
  "limit": "<<pageSize>>"
}

Stop condition

The stop condition is a JavaScript function that determines when to stop fetching pages. It receives two arguments:
  1. response – object containing:
    • data – the parsed response body
    • headers – response headers
  2. pageInfo – object containing:
    • page – current page number
    • offset – current offset
    • cursor – current cursor value
    • totalFetched – total number of items fetched so far
The function should return true to stop pagination, or false to continue. Examples: Stop when no more pages (using response metadata):
(response, pageInfo) => !response.data.has_more;
Stop when data array is empty:
(response, pageInfo) => response.data.items.length === 0;
Stop when cursor is null or missing:
(response, pageInfo) => !response.data.next_cursor;
Stop after fetching a specific number of items:
(response, pageInfo) => pageInfo.totalFetched >= 1000;
Stop when on last page (from total pages header):
(response, pageInfo) =>
  pageInfo.page >= parseInt(response.headers["x-total-pages"] || "1");
Combine multiple conditions:
(response, pageInfo) => {
  const items = response.data.results || [];
  return (
    items.length === 0 || !response.data.next || pageInfo.totalFetched >= 5000
  );
};

Final transform context

The context is a combined view of the entire workflow execution:
  • All step results keyed by step ID
  • The original payload
The sourceData object might look like this:
{
  "payload": { "userId": "12345" },
  "getCustomers": {
    "data": [{ "id": "cus_123", "email": "[email protected]" }]
  },
  "createPaymentIntent": {
    "data": { "id": "pi_456", "status": "succeeded" }
  }
}