Skip to main content

Consumer Gateways

Consumer gateways call an external service and do not return a result. Use script type consumerGateway.

Consumer gateway calls that fail with a transient error are automatically retried with a fixed backoff schedule: after 10 seconds, then 60 seconds, then 15 minutes. If all three attempts fail, the platform writes a system.logEntry with category consumer_gateway_retry and level ERROR, containing the gateway name and the last error message.

Errors that are not retried: HTTP 4xx responses (except 429 Too Many Requests). These indicate a configuration or data problem that retrying cannot resolve.


Mail

Script type: consumerGateway
Gateway name: Mail

Sends a transactional email. The body can be a literal value or rendered from a Handlebars template file.

ParameterTypeRequiredDescription
toEMAILYesRecipient email address
subjectTEXTYesEmail subject line
bodyTEXTBLOCKYesEmail body (HTML or plain text). Use templateFile to render from a template.
ctaHrefTEXTNoCall-to-action URL rendered as a button in the email
ctaNameTEXTNoButton label for the call-to-action link
{
"name": "sendWelcomeMail",
"type": "consumerGateway",
"gateway": "Mail",
"scope": "OBJECT",
"trigger": "MANUAL",
"parameters": [
{ "name": "to", "formula": "emailAddress" },
{ "name": "subject", "value": "Welcome to the platform" },
{ "name": "body", "templateFile": "templates/welcomeMail" },
{ "name": "ctaHref", "formula": "sysInfo(\"appUrl\")" },
{ "name": "ctaName", "value": "Open application" }
]
}

UserInvitation

Script type: consumerGateway
Gateway name: UserInvitation

Grants a person access to the tenant by creating a user invitation for the specified email address.

ParameterTypeRequiredDescription
emailEMAILYesEmail address to invite
firstNameTEXTNoFirst name of the invitee
lastNameTEXTNoLast name of the invitee
personRefTEXTNoReference to an existing person object (e.g. /persons/{uuid})
roleTEXTNosystem.profileRole key to assign to the invited user (admin/member/viewer). Defaults to member when omitted.

Behaviour

The gateway looks up whether a user profile already exists for the given email address:

  • No existing profile — a new user profile is created and an invitation email is sent.
  • Existing profile, no active access — the person object is linked to the existing profile, firstName and lastName are updated if provided, and an invitation email is sent.
  • Existing profile with active access — the person object is linked to the existing profile and firstName/lastName are updated if provided. No invitation record is created and no email is sent.

role is only applied when creating a new profile, or filling in a missing role on an existing one — it never overwrites a role already assigned to an existing profile, even on re-invite.

The gateway runs as a background process. There is no return value or inline feedback available to the calling script.


Webhook

Script type: consumerGateway
Gateway name: Webhook

Sends an HTTP request to a configurable endpoint when a trigger fires. Use this to notify external systems of data changes without writing a custom integration.

Parameters:

NameTypeRequiredDescription
urlTEXTYesTarget URL. Must be HTTPS.
methodTEXTNoHTTP method (GET, POST, PUT, etc.). Default: POST.
payload_<name>TEXTNoA payload field. For POST/PUT: assembled into a flat JSON object body. For GET: appended as a query parameter.
header.<name>TEXTNoAdditional request header (e.g. header.Authorization).

All payload values are serialized as strings. Only TEXT values are supported for payload and header parameters.

{
"name": "notifyWebhook",
"type": "consumerGateway",
"gateway": "Webhook",
"scope": "OBJECT",
"trigger": "UPDATE",
"parameters": [
{ "name": "url", "formula": "systemSetting('rulebooks_webhook_url')" },
{ "name": "method", "value": "POST" },
{ "name": "payload_eventType", "value": "COMPANY_UPDATED" },
{ "name": "payload_href", "formula": "_href" }
]
}

This fires on every UPDATE and POSTs {"eventType":"COMPANY_UPDATED","href":"..."} to the configured URL.


DatastoreReplicator

Script type: consumerGateway
Gateway name: DatastoreReplicator

Replicates a configured subset of an object's fields to an external analytical datastore so customers can build dashboards (Looker Studio, Power BI) and run analyses without burdening the operational database.

The gateway does not write to the external store on every trigger. Events are buffered and flushed as micro-batches at a configurable interval. The default flush interval is 15 minutes; override per destination with _flushInterval.

Supported platforms: Google BigQuery (via load jobs).

Tenant setup

The script definition is part of the model and can be built without any GCP configuration in place. The setup below is performed by the tenant administrator when activating the integration on a live tenant.

  1. GCP project — use your own GCP project (BigQuery is billed against your quota, not the platform's).

  2. Enable the BigQuery API in the GCP project.

  3. Create a service account with the following roles on the target dataset:

    • BigQuery Job User (project level)
    • BigQuery Data Editor (dataset or table level)
  4. Download the service account key as JSON.

  5. Store the key as a secret system setting — do NOT use a plain system.systemSetting, because its value field is readable back via the API. Use system.secretValue instead:

    POST /system/{tenantId}/secretValues
    {
    "name": "bigquery-credentials",
    "secretValue": "<paste the full service account JSON here>"
    }

    The secretValue type stores the value encrypted and excludes it from all API responses. During formula resolution (including gateway parameter evaluation) systemSetting('bigquery-credentials') returns the unmasked value.

  6. Optionally store _projectId and _dataset as plain system.systemSetting objects if they are shared across multiple classes.

If the connection parameters are not (yet) configured on the tenant, the gateway silently skips buffering — no error is thrown and no data is written.

Parameters

Parameters beginning with _ are connection/routing configuration. Every other parameter becomes a column in the target table; its name is the column name and its value is a formula-resolved field or expression.

NameTypeRequiredDescription
_typeTEXTYesTarget platform. Currently only BIGQUERY.
_projectIdTEXTYesGCP project ID that owns the BigQuery dataset.
_datasetTEXTYesBigQuery dataset name.
_tableTEXTYesBigQuery table name. Created automatically on first flush if it does not exist.
_credentialsTEXTYesService account key JSON. Always resolve from a secretValue via systemSetting().
_flushIntervalTEXTNoOverride the flush interval (e.g. 15m, 3h). Default: 15 minutes. Uses Spring duration notation.
<columnName>anyNoAny non-_ parameter becomes a column. The column name is the parameter name; the value is the resolved formula.

The standard column id (the object's UUID) is always written automatically.

Type mapping

Platform value typeBigQuery type
INTEGERINT64
DECIMAL, CURRENCY, PERCENTAGENUMERIC
BOOLEANBOOL
DATEDATE
DATETIMETIMESTAMP
TIMETIME
All othersSTRING

The table schema is derived from the column values on first flush and extended automatically when new columns appear (ALTER TABLE … ADD COLUMN). Removing a column from the script does not drop it from the table.

Triggers

Supports CREATE, UPDATE, and DELETE. On DELETE the gateway issues a hard delete by id on the target table. Because the object no longer exists at execution time, only the id is available — no field formulas are evaluated.

{
"name": "syncToAnalytics",
"type": "consumerGateway",
"gateway": "DatastoreReplicator",
"scope": "OBJECT",
"trigger": ["CREATE", "UPDATE", "DELETE"],
"parameters": [
{ "name": "_type", "formula": "systemSetting('bq.type')" },
{ "name": "_projectId", "formula": "systemSetting('bq.projectId')" },
{ "name": "_dataset", "formula": "systemSetting('bq.dataset')" },
{ "name": "_table", "value": "invoices" },
{ "name": "_credentials", "formula": "systemSetting('bq.credentials')" },
{ "name": "status", "formula": "status" },
{ "name": "amount", "formula": "amount" },
{ "name": "invoiceDate", "formula": "invoiceDate" },
{ "name": "clientName", "formula": "client.legalName" }
]
}

Relations are not a special case — map any relation field using a formula (client.legalName, country.isoCode, etc.) as a regular column. If you need a tenantId column, add it explicitly: { "name": "tenantId", "formula": "sysInfo(\"tenantId\")" }.

Notes

  • _projectId, _dataset, and _table may be plain system.systemSetting objects. _credentials must always be a system.secretValue.
  • Each flush de-duplicates to the latest record per id before writing, so rapid CREATE/UPDATE sequences result in a single row at the destination.
  • If a flush fails, events are retained and retried automatically on the next flush cycle.