Skip to content

Variables and types

Every value you reference in a spec — a field on the source model, an enrichment, a constant, the result of an earlier upsert — has a type (string, number, datetime, agent identity, …). The Spec Builder uses those types to autocomplete what's available where you're typing, and to warn you when you're about to wire something that doesn't fit. This page is the reference for what types exist, where variables come from, and what the type system protects you from.

Where variables come from

In a fresh spec, four things put variables in scope:

  • The source model — the record the processor is working on, plus any enrichments you've added (which appear as nested fields on the source model). The variable is named after the var you set on the source model in the spec config; the default name is whatever the source model's identifier suggests, like event for ghl_opportunity_stage_change or dossier for cube.
  • Constants — any constants you've declared in the spec config. Each appears in scope under its own name.
  • Earlier operations with return_as — every operation that captures a result (upsert_*, define_variable, lock_record_field, insert_contact_period_change) adds a variable in scope from that point forward. Whatever name you set in return_as is what the rest of the spec uses to reference it.
  • The iteration item inside for_each — when the cursor is inside the body of a for_each block, the current item is in scope under whatever name you typed for item_var (e.g. field, line, membership). It disappears once the loop ends.

The Spec Builder's scope panel shows you exactly what's available at the currently selected step. Variable pickers and expression-builder fields autocomplete from that same scope.

Types

Every variable and every expression has a type. Here's the full set, grouped by what they're for:

Primitives

  • string — plain text. Most source fields land here.
  • number — integers and decimals. Used for amounts, durations, IDs that happen to be numeric.
  • booleantrue or false. The result of every condition expression (equals, is_empty, and, …).
  • datetime — a fully-parsed point in time. Source fields almost never start as datetime — they're strings or timestamps until you wrap them with parse_datetime or parse_timestamp.

Structural

  • object — a nested record with named child fields. An enrichment becomes an object on the source model (e.g. event.ghl_opportunity is an object with id, name, status, …).
  • array — a list of items. Used for things like a contact's custom fields or an order's line items. Iterate over it with for_each.

Identity types

These are what upsert_*_external_identity operations hand back when you set return_as. Operations like upsert_activity and upsert_sale expect these types in their identity fields, not plain strings — so you almost always capture an identity with return_as and then pass the captured variable along.

  • agent_external_identity
  • team_external_identity
  • project_external_identity
  • segment_external_identity
  • proposition_external_identity

Record

  • record — what data-upsert operations and define_variable hand back. The result of an upsert_activity is a record, captured with return_as and passed to lock_record_field or read for fields like activity.contact_key.

How the Spec Builder uses types

Two places where the type system actively helps you:

Autocomplete. When you click into a variable picker or an expression-builder field, the dropdown only suggests variables whose type fits what the field expects. Pickers for an agent_external_identity field won't surface plain string variables; pickers for a datetime field won't surface unparsed string fields.

Type warnings on save. When you save a spec, the validator checks that every wiring is compatible: that the expression you put into a datetime field actually returns a datetime, that the variable in an agent_external_identity slot is in fact an agent identity, and so on. Mismatches block the save and surface inline so you can see exactly which step is wrong.

You can usually convert between types with an expression where conversion is well-defined: a string that looks like a timestamp goes through parse_timestamp to become a datetime; a record from upsert_activity exposes its fields by path access (activity.contact_key is a string).

End-customer personal data

Some fields on source models are marked as containing end-customer personal data — a contact's name, email, phone, gender. SalesDash doesn't store those values in any record it writes, and the validator blocks any spec that tries to reference them: in expressions, in operation fields, anywhere.

The variable picker shows these fields greyed out and doesn't let you select them. If you do reference one (e.g. by typing the path manually), the spec fails to save with a clear error pointing at the offending field.

This isn't a configurable rule — it's enforced by the source model's field schema, declared by SalesDash. The aim is that no end-customer personally-identifiable information ever ends up inside SalesDash, even by accident, regardless of how a spec is wired.

If you find a source model exposing a field that should be marked as personal data and isn't, flag it to support — that's a schema correction, not a spec change.