Skip to main content

The fallback is required

fallback is the content you would show without Curious: your control. It serves when the deadline (decideTimeoutMs, 1.5 seconds by default) passes, when the request fails with any status, and when the response’s content does not have the fallback’s shape (reported as content_shape_mismatch). It also types the result: result.content has the fallback’s type, so result.content.subject is a string whether Curious or the fallback served. decide() never throws by default and never retries. A retry would create a second decision; if you must retry, pass the same idempotencyKey and the retry replays the original.

The result

When served is "fallback", error says why (timeout, network_error, server_error, undeclared_element, content_shape_mismatch, …) and the id fields are null. Log error.code and requestId; see Fallbacks and timeouts.

The declaration

declaration carries the secret-key declaration fields in camelCase, converted to the wire names for you: elementType, control, arms, rewards (with windowSeconds, baselineRate), constraints (with thresholdType, windowSeconds), requirements (forbiddenPhrases, requiredPhrases), instructions, format, variables, parameters, and options (assignment, approval, holdout, minimumEffectSize). format, content and parameters pass through in their wire shape. Send the same declaration on every call, or move it to declare(); see Declarations.

Several elements at once

Sends the batch form and returns one result per element, in order. A failing item serves its own fallback with its own error; the envelope-level failure, such as a timeout, serves every fallback. See Batch calls.

Making it throw

throwOnError: true on the client makes decide() throw a CuriousError instead of serving the fallback. It exists for scripts and tests that want a loud failure. Never use it in a delivery path.