Skip to main content

Encoding plans

The encoding surface manages versioned EncodingPlan documents per tenant. These plans are stored with a numeric version and a status.

What an encoding plan is

An encoding plan tells Esper how to turn a raw incoming request into clean, named fields that the rest of the product can use.

If the incoming request is the raw material, the encoding plan is the translation layer.

When to use this page

Use Encoding when:

  • a rule needs a field that does not exist yet
  • the raw request shape is messy or inconsistent
  • you want to derive a stable identity or action from multiple raw inputs

Version metadata

Each encoding config currently has:

  • tenant_id
  • version
  • status
  • config
  • created_at

Supported statuses:

  • Draft
  • Active
  • Retired

What the version fields mean

FieldPlain-English meaningWhy you care
tenant_idThe workspace this encoding version belongs toKeeps data models isolated per tenant
versionThe numbered revision of the encoding planLets you reference a specific plan in replay jobs
statusWhether the version is draft, active, or retiredSignals whether it should be the current plan
configThe actual extraction planContains all runtime, field, and derived logic
created_atWhen the version was createdUseful for rollout history

Plan structure

An encoding plan contains:

  • summary
  • runtime
  • fields
  • derived_fields
  • actions

What each section is for

SectionPlain-English meaning
summaryShort explanation of what this version is meant to do
runtimeGlobal rules for how Esper should treat incoming data
fieldsDirect extractions from the request
derived_fieldsNew fields built from existing extracted values
actionsCanonical action labels such as login_attempt

Runtime policy

The current runtime config supports:

  • flatten_objects
  • normalize_field_names
  • dedup_ttl_seconds
  • entity_state_ttl_seconds
  • session_state_ttl_seconds
  • window_state_ttl_seconds
  • session_bucket_minutes
  • entity_identity_fields
  • default_decision

Supported default decisions:

  • Allow
  • Observe
  • Challenge
  • Block

What the important runtime fields mean

FieldPlain-English meaningWhy it matters
flatten_objectsBreak nested request objects into flatter field pathsHelps when payloads are deeply nested
normalize_field_namesMake field naming more consistentReduces rule-writing mistakes caused by naming drift
dedup_ttl_secondsHow long duplicate events should be treated as duplicatesHelps reduce noisy reprocessing
entity_state_ttl_secondsHow long entity-level state should be keptAffects long-term memory of a user or identity
session_state_ttl_secondsHow long session-level state should be keptAffects short-term flow tracking
window_state_ttl_secondsHow long window counters stay relevantControls short rolling activity checks
session_bucket_minutesHow Esper groups activity into sessionsAffects session boundaries
entity_identity_fieldsWhich fields define a unique entityCritical for stable identity tracking
default_decisionFallback outcome when no rule changes itBest starting point is usually Observe

Extracted fields

Each extracted field includes:

  • name
  • description
  • selector
  • parser
  • normalizers
  • required

Supported selector kinds:

  • RequestField
  • Header
  • QueryParam
  • Cookie
  • RouteParam
  • Method
  • Path
  • SourceId
  • ObservedAt

Supported parsers:

  • String
  • Symbol
  • Bool
  • U64
  • I64
  • F64
  • IpAddress
  • Timestamp
  • Duration
  • Json

Supported normalizers:

  • Trim
  • Lowercase
  • Uppercase
  • CollapseWhitespace

How to think about extracted fields

Each extracted field answers:

  • what do I want to name this value?
  • where do I read it from?
  • how should I parse it?
  • should I normalize it before rules use it?

For a new setup, start with only a few high-value fields:

  • user identity, such as email
  • action type, such as login_attempt
  • source or request metadata, such as method or path

Derived fields and actions

Derived fields and actions both use the same expression family. The current UI supports these derived expression kinds:

  • FieldRef
  • Constant
  • FirstNonEmpty
  • Concat
  • Conditional

Conditionals use these predicate kinds:

  • FieldEq
  • FieldContains
  • FieldExists
  • And
  • Or
  • Not

Plain-English examples

  • A derived field might create device_key by combining multiple values.
  • An action might map several raw request shapes to one stable label such as password_reset.
  • A conditional expression lets you say "if this field looks like X, label it as Y; otherwise use Z."

Example plan

This example matches the app’s default starter plan:

{
"summary": "Flexible request extraction for login traffic",
"runtime": {
"flatten_objects": true,
"normalize_field_names": true,
"dedup_ttl_seconds": 60,
"entity_state_ttl_seconds": 3600,
"session_state_ttl_seconds": 1800,
"window_state_ttl_seconds": 300,
"session_bucket_minutes": 15,
"entity_identity_fields": ["email", "action"],
"default_decision": "Observe"
},
"fields": [
{
"name": "email",
"description": "Email extracted from the request payload",
"selector": {
"kind": "RequestField",
"path": "body.request.email"
},
"parser": "String",
"normalizers": ["Trim", "Lowercase"],
"required": true
}
],
"derived_fields": [
{
"name": "device_key",
"description": "Canonical derived device key",
"expression": {
"kind": "Concat",
"delimiter": ":",
"parts": [
{ "kind": "FieldRef", "field_name": "email" },
{ "kind": "Constant", "value": "browser" }
]
},
"parser": "String",
"normalizers": ["Trim"]
}
],
"actions": [
{
"field_name": "action",
"description": "Tenant-defined action extracted from the request",
"expression": {
"kind": "Constant",
"value": "login_attempt"
},
"parser": "Symbol",
"normalizers": ["Lowercase"]
}
]
}

Frontend API contract

GET /tenants/{tenant_id}/encoding
POST /tenants/{tenant_id}/encoding/versions
PATCH /tenants/{tenant_id}/encoding/versions/{version}
DELETE /tenants/{tenant_id}/encoding/versions/{version}

Good operating guidance

  • Keep your first encoding plan small and easy to explain.
  • Prefer stable business names over raw request names.
  • Use replay jobs after major encoding changes so you can see how historical traffic would be interpreted.