Consent

Every contribution starts with an explicit, informed yes. Relay builds the consent screen from the schema you selected, so it always matches what is actually sent.

The built-in dialog

presentConsent() renders an accessible dialog from your app's active consent version and resolves with the user's choice.

client
const choice = await relay.presentConsent(); // "accepted" | "declined" | "dismissed"

The dialog:

  • Names your app, your organisation and Relay.
  • Lists the exact events and fields that would be shared, and what is never collected.
  • States the purposes, for example AI model training or evaluation.
  • Explains that approved buyers may receive licensed contributions under separate agreements.
  • Explains withdrawal and deletion, including their limits.
  • Gives accept and decline the same size and style. Escape dismisses without deciding.

Pass { container } to mount the dialog inside a specific element.

Your own UI

To match your app's design, fetch the presentation and render it yourself. You must show the copy and field list as provided.

client
const presentation = await relay.getConsent();
// Render presentation.copy and presentation.fields in your own UI.

await relay.accept(presentation);   // records the opt-in and starts collection
relay.decline(presentation);        // remembers the decision, sends nothing
Keep the choice fair

Accept and decline must be equally prominent. Don't pre-select, nag, or make your app work differently for people who decline.

Consent versions and renewal

Each consent version is hashed over the schema and purposes. If you publish a version with a different hash, that is a material change: Relay stops accepting events from existing contributors and the SDK moves to renewal_required until they opt in again. Wording-only changes keep the same hash and need no renewal.

States

StateMeaning
not_askedThe user hasn't decided yet. Nothing is collected.
declinedThe user said no. Nothing is collected.
resumingRestoring an earlier opt-in on page load.
activeOpted in. Approved events are collected.
renewal_requiredThe consent scope changed. Ask again before collecting.
withdrawnThe user withdrew. Collection stopped and the queue was cleared.
unavailableCollection isn't currently permitted for this app.

Use onStateChange to keep your UI in sync, or read relay.getState() at any time.

What is remembered

The SDK remembers only the decision, in localStorage by default, never an identifier. Pass storage: null to keep it in memory only. All requests use credentials: "omit", so no cookies are sent or set.