For the complete documentation index, see llms.txt. This page is also available as Markdown.

Golden Signals

Read ContextSDK's on-device Golden Signals to enrich ad requests, either instantly or after waiting for a full sensor window.

Golden Signals are a snapshot of the device's current context - motion, device state, and the outputs of ContextSDK's on-device models - returned as a plain dictionary you can attach to an ad request. Everything is computed on device.

Golden Signals are only available in an AdTech build of ContextSDK, distributed per ad network. If goldenSignals doesn't exist on ContextManager in your integration, you're on a standard build - talk to us and we'll get you the right one.

Choosing between the two methods

There are two ways to read the signals, and the difference is only in how they treat the motion sensor:

Method
Behaviour

goldenSignals()

Returns immediately, reading whatever the accelerometer has buffered at that instant.

fetchGoldenSignals()

Waits until a full sensor window has been collected, then calls you back.

The distinction matters because the accelerometer buffer starts out empty at every app launch and every return to the foreground, and takes a few seconds to fill. Ad requests tend to cluster in exactly that window - interstitial and rewarded preloads usually fire moments after launch or resume - so goldenSignals() called there returns a thinner dictionary, with motion-derived signals and any model outputs that depend on them missing or based on a partial window.

Use goldenSignals() when you cannot afford to wait at all. Use fetchGoldenSignals() when you would rather wait a moment for complete data - it is the better default for a bid request that isn't already at its deadline.

Reading the signals instantly

let signals = ContextManager.goldenSignals(
    trigger: "before_bid_request",
    bidID: "your-bid-id"
)
// Attach `signals` to your ad request

Every parameter is optional - see Tagging the request.

Waiting for a full sensor window

fetchGoldenSignals() calls your closure once, on the main thread, with a status and the signals:

The status tells you how complete the sensor data behind the dictionary is:

Status
Meaning

.ok

A full sensor window was collected. This is the case the method exists to guarantee.

.timeout

The call ran out of time before the sensor delivered a full window. The dictionary holds the instantly-available data instead - the same thing goldenSignals() would have returned.

.sensorUnavailable

The device has no usable motion sensor, or you're running in the simulator. Motion signals are flagged invalid and the model-driven results that depend on them are absent.

A dictionary always comes back - the call never returns empty-handed, and never blocks indefinitely. If you have no use for the distinction, you can ignore the status entirely and treat the method as a drop-in for goldenSignals().

What a non-.ok status does mean is that the dictionary is less complete. Anything derived from motion needs a usable window to be computed from, so on .timeout and .sensorUnavailable the motion signals and the model-driven results that consume them (u_, m_ and mv_ keys) may be based on a partial window or missing altogether. Read keys defensively rather than assuming a fixed set - which is good practice anyway, since the exposed set is configured remotely.

The call is bounded: it gives up after at most 10 seconds and reports .timeout, so it will never block an ad request indefinitely.

Tagging the request

Both methods take the same optional parameters, and all of them are worth setting:

  • trigger - where in your flow you're reading the signals, for example "before_bid_request" or "before_ad_show". Use a distinct value per call site.

  • bidID - your identifier for the bid. Setting it lets us connect the several contexts captured across one bid's lifecycle, which is what makes stage-by-stage analysis possible.

  • customSignals - anything you know about this request that we can't measure on device. See Attaching custom signals.

The dictionary also comes back with a ctxId key: a short identifier for this specific context. Keep it alongside your own records if you want to join your reporting back to a single Golden Signals read.

Attaching custom signals

Both methods accept custom signals for that one call - the ad format you're about to request, how many impressions this session has already shown, whatever else describes the request:

These are layered on top of any global signals you've set with setGlobalCustomSignal(id:value:), and a per-call signal wins over a global one with the same id. See Custom Signals for the rules an id and value must follow, and for how to set globals.

They are used in two places: they're recorded on the context event we log for this call, so your bid data and ours can be joined on ctxId, and they're available to the on-device models as features - so a model trained on one of your custom signals can act on it.

What's in the dictionary

Keys are stable strings and values are heterogeneous, so read each key as the type that signal carries:

Key pattern
Contents

ctxId

A String identifying this context.

contextDuration

An Int: the sensor window length in seconds, when enabled for your app.

c<number>

An individual signal. The type follows the signal - Bool, Int, Float, Double, Decimal, String, [String] or [Float].

u_<name>

A Float probability from an on-device activity model.

m_<name>

An on-device model output: a Double score, or a [Float] vector for a moment embedding.

mv_<name>

A String version of the model that produced the matching m_ embedding.

Keys that aren't available for a given call are omitted rather than returned empty, so check for a key's presence rather than assuming a fixed set. Which signals and model outputs are exposed is configured remotely per app, so the set can change without an app update.

Last updated

Was this helpful?