Integration Templates & Revisions
Integration templates are org-level reusable integration definitions. Each template can have multiple immutable revisions, allowing you to version configuration changes over time while maintaining a full audit trail.
Architecture
Integration Template (org-level, reusable)
└── Revision v1 (immutable config snapshot)
└── Revision v2 (immutable config snapshot)
└── Revision v3 (immutable config snapshot)
Pipeline A → uses Template at Revision v3
Pipeline B → uses Template at Revision v2Templates can be created manually or installed from the Integration Catalog. Once created, templates are assigned to pipelines via Pipeline Integrations.
Integration Template Object
{
"id": "tmpl_abc123",
"org_id": "org_xyz",
"name": "Meta CAPI Production",
"vendor": "meta_capi",
"description": "Meta CAPI for our e-commerce properties",
"catalog_vendor_key": "meta_capi",
"catalog_variant_key": "retail",
"created_at": "2026-02-20T10:00:00Z",
"updated_at": "2026-02-25T14:30:00Z"
}| Field | Type | Description |
|---|---|---|
id | string | Unique template identifier |
org_id | string | Organisation identifier |
name | string | Display name (unique within org) |
vendor | string | Vendor key (e.g. meta_capi, ga4, google_ads) |
description | string | Template description |
catalog_vendor_key | string | Catalog vendor key (if created from catalog) |
catalog_variant_key | string | Catalog variant used (if created from catalog) |
created_at | string | ISO 8601 creation timestamp |
updated_at | string | ISO 8601 last-updated timestamp |
Integration Revision Object
{
"id": "rev_xyz789",
"integration_id": "tmpl_abc123",
"org_id": "org_xyz",
"revision_number": 3,
"config": {
"pixel_id": "1234567890",
"access_token": "EAABsb...",
"batch_size": 100,
"hash_pii": true
},
"delivery_mode": "server_side",
"change_summary": "Updated access token for Q1 2026",
"created_by": "user_123",
"created_at": "2026-02-25T14:30:00Z"
}| Field | Type | Description |
|---|---|---|
id | string | Unique revision identifier |
integration_id | string | Parent template ID |
org_id | string | Organisation identifier |
revision_number | integer | Auto-incrementing revision number |
config | object | Full integration configuration (vendor-specific) |
delivery_mode | string | server_side or client_side |
change_summary | string | Human-readable description of what changed |
created_by | string | User ID who created this revision |
created_at | string | ISO 8601 creation timestamp |
Revisions are immutable. To change configuration, create a new revision. This ensures a complete audit trail and allows pipelines to pin to specific revisions.
Using Pipeline Parameters in Revision Config
Revision config values can reference pipeline parameters using the {{pipeline_param.KEY}} syntax. This allows a single integration revision to be shared across multiple pipelines, with each pipeline providing its own values for credentials and IDs.
For example, a GA4 revision config with parameter references:
{
"config": {
"measurement_id": "{{pipeline_param.ga4_measurement_id}}",
"api_secret": "{{pipeline_param.ga4_api_secret}}",
"debug_mode": false
},
"delivery_mode": "server_side",
"change_summary": "GA4 config with pipeline parameter references"
}When this revision is assigned to a pipeline, the event processor substitutes {{pipeline_param.ga4_measurement_id}} and {{pipeline_param.ga4_api_secret}} with the values defined in that pipeline’s parameters object. Static values like debug_mode are used as-is.
Reading Connection Values from the Blueprint
A blueprint mapping can read one of the integration’s own connection values with a config.<key> source, where <key> is a field of the integration’s connection config (after pipeline parameters have been substituted). The same form works as a filter or expression path and as a template placeholder:
{
"global": {
"mappings": [
{ "source": "config.mbox_name", "target": "notifications[].mbox.name", "required": true },
{ "target": "conversion", "mode": "template",
"template": "urn:lla:llaPartnerConversion:{{config.conversion_rule_id}}" }
]
}
}The value reaches the vendor payload only where a mapping writes it; the connection config itself is never forwarded. A required mapping whose config key is not set drops the event for that integration with a required_missing record, and default_value applies when the key is missing or blank, exactly as for an event field. An event the blueprint does not map at all (no event section, defaults.action drop) is filtered before any global mapping runs, so a required global mapping never turns an event nobody meant to deliver into a required_missing record.
A mapping can carry a condition, a filter expression over the event such as "context.device.os IN ('iOS', 'iPadOS')": it runs only for events the condition holds for, in any section and any mode, and when it does not hold it reads, writes and requires nothing. Two mappings with opposite conditions send one source to different targets, for example the mobile advertising ID as userData.idfa[] on iOS and userData.gpsaid[] on Android.
Connection fields the catalogue types as secret (API keys, access tokens, signing secrets) cannot be read this way: a config.<key> source naming one resolves as missing. Credentials belong in the request’s auth header, endpoint URL or static fields, which the delivery driver resolves. A vendor whose API requires a credential inside the event body can opt a specific key in through its delivery spec.
Static Values and JSON Types
A static mapping’s value is read as JSON when it is a JSON literal, so it reaches the vendor with that type: "true" and "false" are booleans, "42" and "12.5" are numbers, and "null" is an explicit JSON null. Anything else is sent as text.
Use "null" when a vendor requires a field to be present but empty. AppLovin, for example, requires data on every event and data: null on a page view:
{
"events": {
"page": {
"vendor_event_name": "page_view",
"mappings": [
{ "target": "data", "mode": "static", "value": "null" }
]
}
}
}The null is written even when the target is an object the vendor’s payload groups other fields under: the page view is sent with "data": null, and an event that maps fields into data is sent with the object. This applies only to a static value. A template or expression that evaluates to null writes nothing, as a missing source does.
Hashed Identifiers in One Field
An expression mapping ("mode": "template") can normalise and hash an identifier in one field. A phone number is converted to E.164, then hashed:
{ "target": "@root.user_data.sha256_phone_number", "mode": "template",
"template": "NORMALISED_SHA256(PHONE_E164($traits.phone, 'GB'))" }PHONE_E164(phone, options)returns the number in E.164 (+447700900123). The options are the phone transform’s: a default region such as'GB', or a list such as'region=GB,strip_plus=true'. Usestrip_plus=trueonly for a vendor that wants digits without the+. The region comes from the number’s own country code first, then the event’s country and locale, then the default. An invalid or missing number gives null.NORMALISED_SHA256(value)trims, lowercases and SHA-256 hashes the value, and passes an existing hash through unchanged. A missing or empty value gives null.
An expression that gives null writes nothing, so an event without a valid phone sends no phone field rather than the hash of an empty or invalid value.
Field Scripts
A mapping with "mode": "script" computes its output with JavaScript, for logic a source, template or expression cannot express. The script reads the event as input and returns what the mapping writes:
| The script returns | With a target | Without a target |
|---|---|---|
| A value | Written to the target | Rejected: the mapping writes nothing |
| An object | Written to the target as that object | Each key is an output field, written with its value |
null, or nothing | Nothing is written | Nothing is written |
Leaving the target blank lets one script decide which fields to write. For example, GA4 carries a user property in a different place on each endpoint, so one script in the global event parameters sends the anonymous ID to the right one for every event:
{
"defaults": {
"mappings": [
{ "mode": "script", "target": "",
"script": "if (input.properties.vendor_event_endpoint === 'gc') {\n return { 'up.anonymous_id': input.anonymous_id };\n}\nreturn { '@root.user_properties.anonymous_id.value': input.anonymous_id };" }
]
}
}inputis the event as it stands when the mapping runs, so it includes what earlier mappings in the blueprint wrote: an event section’svendor_event_nameandvendor_event_endpoint, for example, are ininput.properties. It is a copy: changing it changes nothing.- Output names follow the section’s rules for targets. In the global parameters they are placed at the payload root, and a name starting with
@root.is placed at the root from any section. - Every field a script writes is part of the blueprint’s output, so the strict output contract sends it.
sha256,md5,base64Encodeandbase64Decodeare available.sha256andmd5return lowercase hex and treat a missing value as an empty string.- A script runs for at most 100 ms. A script that fails, times out or returns a value with no target writes nothing and the event continues; the failure is logged.
- A
conditiongates a script as it gates any mapping, and arequiredscript with a target drops the event for that integration with arequired_missingrecord when it writes nothing.
In the visual editor a field script is a Custom Script field. The editor shows what the script reads and returns, lists the output fields it names, and runs it on a sample event so you can check what it writes before you save.
Older blueprints can carry event scripts (scripts), which run over the whole event after the mappings. They keep working, but new logic belongs in a field script, which says what it writes and is part of the blueprint’s output.
List Integration Templates
GET /v1/admin/integration-templates
Returns a paginated list of integration templates for the current organisation.
Query parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
limit | integer | 50 | Items per page (max 100) |
offset | integer | 0 | Offset for pagination |
Request:
curl -X GET "http://localhost:8084/v1/admin/integration-templates?limit=20" \
-H "Authorization: Bearer {access_token}"Response:
{
"integrations": [
{
"id": "tmpl_abc123",
"name": "Meta CAPI Production",
"vendor": "meta_capi",
"description": "Meta CAPI for our e-commerce properties",
"catalog_vendor_key": "meta_capi",
"catalog_variant_key": "retail",
"created_at": "2026-02-20T10:00:00Z",
"updated_at": "2026-02-25T14:30:00Z"
}
],
"total": 1,
"limit": 50,
"offset": 0
}Required role: viewer or above.
Create Integration Template
POST /v1/admin/integration-templates
Create a new integration template manually (without using the catalog).
Request:
{
"name": "Custom GA4 Setup",
"vendor": "ga4",
"description": "GA4 for internal analytics"
}| Field | Type | Required | Description |
|---|---|---|---|
name | string | Yes | Display name (must be unique within org) |
vendor | string | Yes | Vendor key (must be a valid vendor in the catalog) |
description | string | No | Description |
Response (201 Created): Full template object.
To create a template with pre-filled configuration, use the Install from Catalog endpoint instead.
Required role: editor or above.
Get Integration Template
GET /v1/admin/integration-templates/{id}
Retrieve a single integration template by ID.
Request:
curl -X GET http://localhost:8084/v1/admin/integration-templates/tmpl_abc123 \
-H "Authorization: Bearer {access_token}"Response: Full template object.
Required role: viewer or above.
Update Integration Template
PUT /v1/admin/integration-templates/{id}
Update metadata on an integration template. Only name and description can be updated.
Request:
{
"name": "Meta CAPI - E-commerce",
"description": "Updated description"
}Response: Updated template object.
Required role: editor or above.
Delete Integration Template
DELETE /v1/admin/integration-templates/{id}
Soft-delete an integration template.
Request:
curl -X DELETE http://localhost:8084/v1/admin/integration-templates/tmpl_abc123 \
-H "Authorization: Bearer {access_token}"Response:
{
"status": "deleted"
}Deletion will fail if the template is still assigned to any pipeline. Remove all pipeline assignments first.
Required role: admin or above.
Revisions
List Revisions
GET /v1/admin/integration-templates/{id}/revisions
Returns all revisions for an integration template, ordered by revision number descending (newest first).
Request:
curl -X GET http://localhost:8084/v1/admin/integration-templates/tmpl_abc123/revisions \
-H "Authorization: Bearer {access_token}"Response:
{
"revisions": [
{
"id": "rev_xyz789",
"integration_id": "tmpl_abc123",
"revision_number": 3,
"config": { "pixel_id": "1234567890", "batch_size": 100 },
"delivery_mode": "server_side",
"change_summary": "Updated access token",
"created_by": "user_123",
"created_at": "2026-02-25T14:30:00Z"
},
{
"id": "rev_abc456",
"integration_id": "tmpl_abc123",
"revision_number": 2,
"config": { "pixel_id": "1234567890", "batch_size": 50 },
"delivery_mode": "server_side",
"change_summary": "Increased batch size",
"created_by": "user_123",
"created_at": "2026-02-22T09:00:00Z"
}
]
}Required role: viewer or above.
Create Revision
POST /v1/admin/integration-templates/{id}/revisions
Create a new immutable revision with updated configuration.
Request:
{
"config": {
"pixel_id": "1234567890",
"access_token": "EAABsb_new_token...",
"batch_size": 100,
"hash_pii": true
},
"delivery_mode": "server_side",
"change_summary": "Rotated access token for Q1 2026"
}| Field | Type | Required | Description |
|---|---|---|---|
config | object | Yes | Full integration configuration |
delivery_mode | string | Yes | server_side or client_side |
change_summary | string | No | Description of what changed |
Response (201 Created): Full revision object with auto-assigned revision_number.
Creating a new revision does not automatically update any pipelines. To deploy the new revision, update the pipeline integration assignment to point to the new revision ID.
Required role: editor or above.
Get Revision
GET /v1/admin/integration-templates/{id}/revisions/{rev}
Retrieve a specific revision by revision number.
Request:
curl -X GET http://localhost:8084/v1/admin/integration-templates/tmpl_abc123/revisions/3 \
-H "Authorization: Bearer {access_token}"Response: Full revision object.
Required role: viewer or above.