Management APIIntegration Templates

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 v2

Templates 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"
}
FieldTypeDescription
idstringUnique template identifier
org_idstringOrganisation identifier
namestringDisplay name (unique within org)
vendorstringVendor key (e.g. meta_capi, ga4, google_ads)
descriptionstringTemplate description
catalog_vendor_keystringCatalog vendor key (if created from catalog)
catalog_variant_keystringCatalog variant used (if created from catalog)
created_atstringISO 8601 creation timestamp
updated_atstringISO 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"
}
FieldTypeDescription
idstringUnique revision identifier
integration_idstringParent template ID
org_idstringOrganisation identifier
revision_numberintegerAuto-incrementing revision number
configobjectFull integration configuration (vendor-specific)
delivery_modestringserver_side or client_side
change_summarystringHuman-readable description of what changed
created_bystringUser ID who created this revision
created_atstringISO 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'. Use strip_plus=true only 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 returnsWith a targetWithout a target
A valueWritten to the targetRejected: the mapping writes nothing
An objectWritten to the target as that objectEach key is an output field, written with its value
null, or nothingNothing is writtenNothing 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 };" }
    ]
  }
}
  • input is the event as it stands when the mapping runs, so it includes what earlier mappings in the blueprint wrote: an event section’s vendor_event_name and vendor_event_endpoint, for example, are in input.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, base64Encode and base64Decode are available. sha256 and md5 return 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 condition gates a script as it gates any mapping, and a required script with a target drops the event for that integration with a required_missing record 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:

ParameterTypeDefaultDescription
limitinteger50Items per page (max 100)
offsetinteger0Offset 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"
}
FieldTypeRequiredDescription
namestringYesDisplay name (must be unique within org)
vendorstringYesVendor key (must be a valid vendor in the catalog)
descriptionstringNoDescription

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"
}
FieldTypeRequiredDescription
configobjectYesFull integration configuration
delivery_modestringYesserver_side or client_side
change_summarystringNoDescription 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.