Return Types & Sentinels
Frozen BucketedVariation/BucketedFeature, the sentinel contract, enums
This page documents the value objects the decision methods return, the enums you pass in, and — most importantly — the sentinel return contract: how the SDK signals a business miss by value instead of by exception. The decision methods never raise and never return a bare nil for a business miss.
All types live under the ConvertSdk module.
The sentinel return contract
Decisioning methods return a frozen value object on every call:
- A hit returns a frozen
BucketedVariation(orBucketedFeature):#keyis the real key,#error?isfalse. - A miss returns a frozen
Sentinel:#keyis alwaysnil,#error?is alwaystrue, and#to_sis the wire string.
This is architecture Decision 2: misses are signaled by value, never by exception. It is why a single case branch handles both outcomes:
case (variation = context.run_experience("homepage-test")).key
when nil then render_default # Sentinel — a business miss
else render_variation(variation.key)
endSentinel implements exactly three methods:
| Method | Returns |
|---|---|
#to_s | The byte-identical JS wire string (appears in payloads/logs). |
#key | Always nil — so case variation&.key falls through to the nil branch. |
#error? | Always true — the value-object discriminator distinguishing a sentinel from a real decision. |
Sentinels are exposed as frozen singleton constants, so you can branch on object identity for granular handling:
result = context.run_experience("homepage-test")
case result.key
when nil
show_default if result.equal?(ConvertSdk::RuleError::NO_DATA_FOUND)
else
render_variation(result.key)
endEquality is left as default object identity (no
==override). Two distinctSentinelinstances built from the same wire string are not equal — which is why the canonical instances live as shared frozen constants.
The sentinel constants
| Constant | Wire string |
|---|---|
ConvertSdk::RuleError::NO_DATA_FOUND | convert.com_no_data_found |
ConvertSdk::RuleError::NEED_MORE_DATA | convert.com_need_more_data |
ConvertSdk::BucketingError::VARIATION_NOT_DECIDED | convert.com_variation_not_decided |
RuleError sentinels signal rule/audience-evaluation misses; BucketingError::VARIATION_NOT_DECIDED signals that no variation could be bucketed. The wire strings are byte-identical to the JS SDK.
Never-raises guarantee
run_experience returns RuleError::NO_DATA_FOUND on an internal failure (plus an error log line); run_experiences and run_features degrade to []; run_feature degrades to a DISABLED BucketedFeature. A raising collaborator degrades the call — it never crashes your host request (NFR9).
BucketedVariation
BucketedVariationReturned by run_experience (hit) and as the elements of run_experiences. A frozen Struct with snake_case members, aligned to the JS BucketedVariation shape:
ConvertSdk::BucketedVariation.new(
experience_id:,
experience_key:,
experience_name:,
bucketing_allocation:,
id:,
key:,
name:,
status:,
traffic_allocation:,
changes:
)key— the variation key from the Convert dashboard; this is what you branch on.id/experience_id— the variation and experience identifiers (used on the bucketing wire event).#error?— alwaysfalse(a real decision is never a business miss).
variation = context.run_experience("homepage-test")
case variation&.key
when nil then render_default # sentinel miss
when "treatment" then render_treatment
else render_variation(variation.key)
endBucketedFeature and FeatureStatus
BucketedFeature and FeatureStatusReturned by run_feature (a single feature, or an Array when several bucketed variations carry the feature) and as the elements of run_features. A frozen Struct with snake_case members:
ConvertSdk::BucketedFeature.new(
experience_id:,
experience_key:,
experience_name:,
id:,
key:,
name:,
status:, # a FeatureStatus wire value
variables: # the feature's variable map
)#error?— alwaysfalse.
A feature miss is not a sentinel — it is a frozen DISABLED BucketedFeature. Branch on #status:
feature = context.run_feature("new-checkout")
if feature.status == ConvertSdk::FeatureStatus::ENABLED
render_new_checkout(feature.variables["headline"])
else
render_legacy_checkout
endFeatureStatus is a module of wire-string constants (byte-identical to the JS SDK):
| Constant | Wire string |
|---|---|
ConvertSdk::FeatureStatus::ENABLED | enabled |
ConvertSdk::FeatureStatus::DISABLED | disabled |
GoalDataKey
GoalDataKeyThe eight platform keys you may attach to a conversion via track_conversion(..., goal_data: { ... }). The public Ruby surface accepts snake_case symbol keys; the SDK translates them to the camelCase wire identifier (the camelCase wire form is also accepted for integrators who already know the platform identifiers). Any key outside these eight is rejected and debug-logged.
| Public snake_case key | GoalDataKey constant | Wire string |
|---|---|---|
amount | AMOUNT | amount |
products_count | PRODUCTS_COUNT | productsCount |
transaction_id | TRANSACTION_ID | transactionId |
custom_dimension_1 | CUSTOM_DIMENSION_1 | customDimension1 |
custom_dimension_2 | CUSTOM_DIMENSION_2 | customDimension2 |
custom_dimension_3 | CUSTOM_DIMENSION_3 | customDimension3 |
custom_dimension_4 | CUSTOM_DIMENSION_4 | customDimension4 |
custom_dimension_5 | CUSTOM_DIMENSION_5 | customDimension5 |
context.track_conversion(
"purchase",
goal_data: { amount: 49.99, products_count: 3, transaction_id: "tx-42" }
)See Tracking Conversions.
LogLevel
LogLevelInteger thresholds (JS-parity) passed as log_level: to ConvertSdk.create. A message emits when its level is >= the configured threshold.
| Constant | Value |
|---|---|
ConvertSdk::LogLevel::TRACE | 0 |
ConvertSdk::LogLevel::DEBUG | 1 |
ConvertSdk::LogLevel::INFO | 2 |
ConvertSdk::LogLevel::WARN | 3 |
ConvertSdk::LogLevel::ERROR | 4 |
ConvertSdk::LogLevel::SILENT | 5 |
The default is DEBUG. See Configuration Options.
SystemEvents
SystemEventsLifecycle event names you can subscribe to with Client#on(event, &block). Each is a String constant on ConvertSdk::SystemEvents (byte-identical to the JS SDK); the matching string works too.
| Constant | Event name |
|---|---|
READY | ready |
CONFIG_UPDATED | config.updated |
BUCKETING | bucketing |
CONVERSION | conversion |
SEGMENTS | segments |
API_QUEUE_RELEASED | api.queue.released |
LOCATION_ACTIVATED | location.activated |
LOCATION_DEACTIVATED | location.deactivated |
AUDIENCES | audiences |
DATASTORE_QUEUE_RELEASED | datastore.queue.released |
See Event System.
Next steps
- Code Examples — these types in complete flows
- Feature Flags — the shared feature-flag concept doc