Intunix

Context

Context is arbitrary runtime data you attach to a form — which course, which screen, which plan tier. It feeds targeting, segments your reporting, and can scope cooldowns so the same form can be asked once per value instead of once per user.

Why it exists

Without context, “How was this course?” needs one form per course. With it, you build one form and tell the SDK which course the user is looking at. Responses arrive tagged, the console segments by that tag, and cooldowns can be kept independent per course.

Two ways to attach it

Sticky context

Merges into a session-scoped bag and stays attached to every show and every response until you clear it or a new session begins.

code
client.setContext({ courseId: 'c-101', screen: 'lesson' })

// Merges — screen becomes 'quiz', courseId stays 'c-101'
client.setContext({ screen: 'quiz' })

client.setContext(null)     // clear it
client.clearContext()       // same thing

Use it for facts that stay true while the user is somewhere:

code
// React
'use client'
import { useEffect } from 'react'
import { useSetContext } from '@intunix/react'

export function CoursePage({ courseId }: { courseId: string }) {
  const setContext = useSetContext()
  useEffect(() => { setContext({ courseId }) }, [courseId, setContext])
  // ...
}

Per-call context

Scoped to a single showForm() call, and it wins over sticky context for that call.

code
client.showForm('course_csat', { context: { courseId: 'c-102' } })

Allowed values

Context is a flat object of strings, numbers, booleans, and null. No nested objects, no arrays.

code
// Good
client.setContext({ courseId: 'c-101', lesson: 4, isTrial: false })

// Not supported — nested and array values are rejected
client.setContext({ course: { id: 'c-101' }, tags: ['a', 'b'] })
Do not put personal data in context

Context is stored with every response and is visible to anyone with console access to that form's results. Use opaque identifiers — courseId, not studentEmail.

Where it flows

DestinationHow it appears
Response payloadsAs a top-level context object, so the console can segment feedback.
Event batchesAttached to each event entry.
TargetingMatched by rules with kind: 'context'.
CadenceUsed as the cooldown key when frequencyScope: 'form_context'.

Targeting on context

code
// Console config — only ask on the advanced course, and only for trial users
targeting: {
  rules: [
    { kind: 'context', key: 'courseId', op: 'eq', value: 'c-101' },
    { kind: 'context', key: 'isTrial',  op: 'eq', value: true },
  ],
}

exists is useful for “only ask if we know which course they are on”:

code
{ kind: 'context', key: 'courseId', op: 'exists' }

Per-context cooldowns

Set frequencyScope: 'form_context' on the form and the cadence ledger is keyed by the context values named in its context targeting rules — rather than one entry per user.

code
// frequencyScope: 'form_context', cadence answered: 90 days

client.showForm('course_csat', { context: { courseId: 'c-101' } })
// → shows, user answers, c-101 now quiet for 90 days

client.showForm('course_csat', { context: { courseId: 'c-101' } })
// → blocked

client.showForm('course_csat', { context: { courseId: 'c-102' } })
// → shows. Different course, independent cooldown.
Choose the key carefully

A high-cardinality context key — a session id, a timestamp, a random request id — makes every show a fresh cooldown bucket, which effectively disables cadence and turns the form into a nag. Scope on something a user encounters a handful of times, not thousands.

Reporting

In the console, a form's analytics can be filtered by any context key that has been recorded against it. Responses, score breakdowns, and per-question charts all respect the filter, so you can read NPS for one course, one screen, or one plan tier in isolation.

Context keys are discovered from the data — nothing needs declaring up front. Send a new key and it becomes available as a filter once responses carry it.

Lifecycle

  • Sticky context lives in sessionStorage under intunix:context.
  • It is cleared automatically when a new session starts.
  • It is cleared by reset() — see Identity & sessions for why that matters on shared browsers.
  • Per-call context is consumed by that one show and never persisted.