Skip to content

feat: add marketing consent extension for per-channel opt-in#407

Open
wsbrunson wants to merge 4 commits into
Universal-Commerce-Protocol:mainfrom
wsbrunson:feat/marketing-consent-extension
Open

feat: add marketing consent extension for per-channel opt-in#407
wsbrunson wants to merge 4 commits into
Universal-Commerce-Protocol:mainfrom
wsbrunson:feat/marketing-consent-extension

Conversation

@wsbrunson
Copy link
Copy Markdown

@wsbrunson wsbrunson commented May 1, 2026

Description

Note: The original draft of this PR proposed Alternative Option 1: Separate marketing_consent extension as the recommended approach. The description below reflects an updated proposal based on feedback from @jamesandersen.

Evolves the existing buyer_consent extension with per-channel marketing consent capture. Rather than introducing a separate extension, this adds two fields to buyer_consent:

  • marketing_consent_options (checkout level, business → platform): An array of marketing channels the business offers for opt-in, each with a channel identifier, display text, and privacy policy URL. Included in create and update checkout responses only.
  • marketing_channels (on buyer.consent, platform → business): An array of the buyer's per-channel opt-in decisions, submitted at checkout completion.

The marketing boolean on buyer.consent is deprecated but not removed. When the business includes marketing_consent_options in the checkout response, platforms MUST use marketing_channels instead of marketing.

Examples

Business-Requested Consent (Checkout Response)

{
  "id": "checkout_789",
  "status": "ready_for_complete",
  "currency": "USD",
  "buyer": {
    "email": "jane@example.com",
    "consent": {
      "analytics": true,
      "preferences": true,
      "sale_of_data": false
    }
  },
  "marketing_consent_options": [
    {
      "channel": "email",
      "display_text": "Promotional emails and exclusive offers",
      "privacy_policy_url": "https://example.com/privacy"
    },
    {
      "channel": "sms",
      "display_text": "Order updates and deals via text",
      "privacy_policy_url": "https://example.com/privacy"
    }
  ],
  "line_items": [...],
  "totals": [...]
}

Consent Capture (Complete Request)

{
  "buyer": {
    "consent": {
      "analytics": true,
      "preferences": true,
      "sale_of_data": false,
      "marketing_channels": [
        { "channel": "email", "opted_in": true },
        { "channel": "sms", "opted_in": false }
      ]
    }
  },
  "payment": {
    "handler": "dev.ucp.payments.example",
    "details": { "token": "tok_abc123" }
  }
}

Motivation

Marketing opt-in is a standard feature on most e-commerce checkout pages. The existing buyer_consent extension includes a marketing boolean, but this does not cover more advanced use cases for collecting marketing consent:

  • Businesses need per-channel control: Email and SMS consent are legally distinct and require separate opt-in. CAN-SPAM treats email as opt-out while the TCPA requires prior express written consent for SMS, with statutory damages of $500–$1,500 per message.
  • Businesses need to declare what consent they want: The current boolean only flows from platform to business. There is no mechanism for a business to request consent collection.
  • Regulatory compliance: GDPR, CAN-SPAM, and CCPA require explicit, channel-specific consent with a clear privacy policy reference.

Category

  • Capability: New schemas (Discovery, Cart, etc.) or extensions. (Requires Maintainer approval)
  • Documentation: Updates to README, or documentations regarding schema or capabilities. (Requires Maintainer approval)

Checklist

  • I have followed the Contributing Guide.
  • I have updated the documentation (if applicable).
  • My changes pass all local linting and formatting checks.
  • (For Core/Capability) I have included/updated the relevant JSON schemas.

Alternative Approaches

Option 1: Separate marketing_consent Extension

The original proposal in this PR. Creates a new dev.ucp.shopping.marketing_consent extension with a marketing_consent object on the buyer containing options and consents.

Details

This approach adds a new extension schema (marketing_consent.json) and documentation page (marketing-consent.md) alongside the existing buyer_consent extension.

The extension adds a marketing_consent object to the buyer within checkout, containing:

  • options (business → platform): An array of marketing channels the business offers for opt-in.
  • consents (platform → business): An array of the buyer's opt-in decisions per channel.

It supports two flows:

  1. Platform collected consent: The platform sends marketing_consent.consents on create/update.
  2. Business-requested consent: The business returns marketing_consent.options in the checkout response, the platform collects consent and sends it on complete.

Why we moved away from this: Two extensions composing onto the same buyer object via allOf creates schema resolution complexity. A normative rule that marketing_consent supersedes buyer.consent.marketing adds fragmentation. Evolving the existing extension is cleaner.

Examples

Business-Requested Consent (Checkout Response):

{
  "buyer": {
    "email": "jane@example.com",
    "marketing_consent": {
      "options": [
        {
          "channel": "email",
          "display_text": "Promotional emails and exclusive offers",
          "privacy_policy_url": "https://example.com/privacy"
        },
        {
          "channel": "sms",
          "display_text": "Order updates and deals via text",
          "privacy_policy_url": "https://example.com/privacy"
        }
      ]
    }
  }
}

Consent Capture (Complete Request):

{
  "buyer": {
    "marketing_consent": {
      "consents": [
        { "channel": "email", "opted_in": true },
        { "channel": "sms", "opted_in": false }
      ]
    }
  }
}

Option 2: Breaking Change to Buyer Consent Object

Replace marketing: boolean with marketing: marketing_channel_consent on the existing consent object. Cleaner than deprecation but introduces a breaking change.

Details

The reason we decided against this was that there are multiple marketing consent properties we want to add and it is not clear whether any of them would apply to the other types of consents (analytics, sale_of_data).

If this kind of divergence is acceptable, we could propose a breaking change to the Buyer Consent extension and change marketing: boolean to marketing: marketing_channel_consent.

Option 3: Add marketing_consent Field to Existing Buyer Consent

Keep marketing: boolean and add a separate marketing_consent object under buyer or consent. Non-breaking but introduces two sources of truth.

Details

For this option, we would keep:

{
  "buyer": {
    "consent": {
      "marketing": true
    }
  }
}

And then add either under buyer or consent the marketing_consent object with options. This would probably be the least amount of changes and would not be a breaking change, but it would introduce two separate sources of truth for marketing consent. There would still be the marketing: boolean property and there would be marketing_consent: { consents: [...] }, which could conflict with the top-level boolean if not kept in sync by the platform.

One could argue that this is technically true in the current proposal but we believe that including a specific extension for marketing_consent is strong enough signal to the Platform and Business that the buyer.consent.marketing field should be ignored.

If documented correctly, this could be a viable option, but we would only consider it if the governing body was okay with the complexity introduced.

Option 4: Generic Advanced Consent Extension

Create a broader buyer_consent_advanced extension that could extend any consent type, not just marketing.

Details

Instead of making a marketing-specific change, we could create a more generic advanced consent extension. The problem with this approach is that we are not sure how the other types of consents would be extended with more advanced options. We currently only see a use case for marketing, but if there are additional thoughts on this, we could explore this option.

@wsbrunson wsbrunson requested review from a team as code owners May 1, 2026 15:56
@google-cla
Copy link
Copy Markdown

google-cla Bot commented May 1, 2026

Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA).

View this failed invocation of the CLA check for more information.

For the most up to date status, view the checks section at the bottom of the pull request.

@wsbrunson wsbrunson force-pushed the feat/marketing-consent-extension branch 2 times, most recently from 3cbc98a to 7e690b8 Compare May 1, 2026 16:04
@wsbrunson
Copy link
Copy Markdown
Author

@jamesandersen wanted to make you aware of this feature request

@jamesandersen
Copy link
Copy Markdown
Contributor

Great work on this @wsbrunson — this is needed. The regulatory divergence between channels is real and growing: CAN-SPAM treats email as opt-out while the TCPA requires prior express written consent for SMS, with statutory damages of $500–$1,500 per message. A merchant receiving today's marketing: true might be legally safe to email but would face serious TCPA exposure if they sent an SMS based on that same boolean. Per-channel consent isn't a nice-to-have — it's a compliance necessity, and this PR addresses that head on.

This capability has been on my TODO list as well, so I've had a few design ideas brewing. I realize this comment has a lot of feedback — happy to keep iterating here, or I can throw up a counter-proposal PR if that's easier to compare.

1. Extend buyer_consent rather than creating a new extension

I think this should evolve the existing buyer_consent extension rather than introduce a parallel marketing_consent extension. The per-channel consent entries would live at buyer.consent.marketing_channels, with buyer.consent.marketing (boolean) going on a deprecation path:

{
  "buyer": {
    "consent": {
      "analytics": true,
      "preferences": true,
      "sale_of_data": false,
      "marketing_channels": [
        { "channel": "email", "opted_in": true },
        { "channel": "sms", "opted_in": false }
      ]
    }
  }
}

Why:

  • One extension, one capability: Consent is consent. Having dev.ucp.shopping.buyer_consent and dev.ucp.shopping.marketing_consent as separate capabilities that both govern marketing consent creates fragmentation — platforms and businesses need to negotiate and implement two overlapping extensions.
  • No superseding rule needed: The current proposal requires a normative rule that marketing_consent supersedes buyer.consent.marketing when both are advertised. If marketing_channels lives inside buyer.consent, the precedence is local and simple: when marketing_channels is present, marketing (boolean) is ignored.
  • Schema resolution: Both the existing buyer_consent extension and this new marketing_consent extension compose onto the buyer object via allOf. When both are active, the platform must resolve two independent extensions claiming composition over the same checkout property. Keeping everything in buyer_consent avoids this conflict — one extension, one composition, one set of ucp_request annotations for buyer.
  • Clean deprecation path: buyer.consent.marketing (boolean) is deprecated in-place. Existing implementations that only send the boolean continue to work. New implementations adopt marketing_channels. No need to manage two independent extension lifecycles.

2. Business-declared options belong on checkout, not buyer

The business's available marketing channels are a property of the business/checkout, not the buyer. I'd suggest placing the options array at the checkout level (e.g., checkout.marketing_consent_options) rather than under buyer.marketing_consent.options. The buyer's consent decisions belong under buyer.consent.marketing_channels, but what the business offers is business context — similar to how available_payment_instruments lives at the checkout level, not under buyer.

Additionally, normative guidance should specify that businesses SHOULD NOT include options for channels where they already have the buyer's consent (e.g., from a prior purchase, website interaction, or identity-linked account). This keeps the protocol stateless — the platform doesn't need to track subscription state — and it eliminates ambiguity for buyers: if a returning subscriber is presented with a consent prompt and doesn't check the box, does that revoke their existing subscription? The cleanest answer is to not present the prompt in the first place.

3. Channel enum — closed with whatsapp

I'd advocate for keeping a closed enum but adding whatsapp to the initial set. A closed enum is the right call here because both the business and platform need to exchange matching values — an open string invites fragmentation (Email vs e-mail vs email, SMS vs sms vs text). With a closed enum, validation catches mismatches at schema time rather than in production.

That said, the current set of ["email", "sms"] is too narrow. WhatsApp is a primary merchant-to-buyer communication channel in LatAm, India, and Southeast Asia. Starting with ["email", "sms", "whatsapp"] avoids excluding a large portion of the global commerce market from day one. Future channels (RCS, push, etc.) can be added via spec updates — and arguably should require a spec update, since new channels typically bring new regulatory requirements that deserve review.

4. Consent at complete only

I'd suggest dropping the "platform collected consent" flow (consent on create/update) and restricting consent capture to complete only. The business-requested flow is the stronger design — the business declares options in the checkout response, the platform renders consent UI, and the buyer's decisions are submitted at completion alongside payment.

The regulatory argument for this is straightforward: GDPR requires consent to be "freely given" and the TCPA requires consent tied to a concrete action. Consent collected before the buyer commits to a purchase (e.g., at checkout creation) may not meet these bars. Tying consent to the "Place Order" action produces the strongest compliance record.

This also simplifies the extension — one flow, one direction, one timing.

5. buyer on complete

The base buyer_consent extension currently marks buyer as "complete": "omit", which blocks sending any consent at complete time — including the existing booleans (analytics, sale_of_data). If we evolve buyer_consent to support marketing_channels at complete, this is the right moment to change buyer on complete from "omit" to "optional" across the extension. This unblocks consent capture at the point of purchase for all consent types, not just marketing.

This also has a practical efficiency benefit: if the platform has no other reason to update the checkout (no address changes, no shipping selection), it can skip the update call entirely and pass consent on complete — saving a network round-trip.


Hopefully the above helps get us moving toward a good discussion on this topic. Would love to hear your thoughts. @vixdug — wanted to flag this for your input as well, since we've noted marketing consent as an important seller feature.

@jingyli jingyli added this to the Working Draft milestone May 5, 2026
@wsbrunson
Copy link
Copy Markdown
Author

wsbrunson commented May 5, 2026

@jamesandersen First of all, thank you for the detailed feedback! I actually agree or mostly agree with all your points. I'll push up a new commit to address this feedback.

I'll go one-by-one below:

1. Extend buyer_consent rather than creating a new extension

Agreed - the first draft of this proposal was to recommend this option. I hesitated because I wasn't sure how introducing a breaking change would go. It really does make the most sense long-term. Having separate extensions for each type of consent is cumbersome on both Platforms and Businesses.

Your recommendation of deprecate marketing and add marketing_channels allows us to make the change we want to make without having to introduce a breaking change.

2. Business-declared options belong on checkout, not buyer

This makes a ton of sense, especially now that the proposal is moving marketing consent back into the buyer object.

3. Channel enum — closed with whatsapp

I don't disagree with this. I don't have a strong opinion either way, I'm happy to add this to the current proposal and if others have strong opinions we can continue the debate.

4. Consent at complete only

I agree that having just a single flow makes this much simpler to document. Business says what channels they want to capture consent for, Platform renders consent checkboxes, sends back opted_in values for anything checked.

I think in the future if we decide that we want the Platform to be able to collect consent and send it to the Business before the Buisness asks, or if we want to introduce collecting consent at the cart level, we could add them without introducing any breaking changes or interrupting the Business -> Platform flow.

5. buyer on complete

Definitely, I'll include that in my update.

Comment thread docs/specification/buyer-consent.md Outdated
@wsbrunson wsbrunson force-pushed the feat/marketing-consent-extension branch from 5ff77fa to 4af7636 Compare May 5, 2026 15:56
@igrigorik igrigorik self-requested a review May 8, 2026 16:16
wsbrunson added 2 commits May 11, 2026 13:43
Adds dev.ucp.shopping.marketing_consent extension supporting two flows:
platform-collected consent and business-requested consent with per-channel
granularity (email, sms).
Extends the existing buyer_consent extension with per-channel marketing
consent capture instead of a separate extension. Adds marketing_consent_options
as a checkout-level field for business-declared channels, and
marketing_channels on buyer.consent for platform-submitted consent at
checkout completion. Deprecates the marketing boolean. Widens buyer on
complete from omit to optional.
@wsbrunson wsbrunson force-pushed the feat/marketing-consent-extension branch from 5628760 to 1a70603 Compare May 11, 2026 18:44
Replaces the docs-only deprecation section with a schema-level
deprecated flag and transition annotation on the marketing boolean,
per reviewer feedback.
@wsbrunson wsbrunson force-pushed the feat/marketing-consent-extension branch from 1a70603 to 182aeef Compare May 11, 2026 18:45
@draspall draspall added the TC review Ready for TC review label May 14, 2026
@jamesandersen
Copy link
Copy Markdown
Contributor

@wsbrunson thanks for all the updates here! I'll take another pass soon but this is looking much closer to what I think makes senses for both platforms and businesses at the protocol level. I'll also be soliciting some more eyes on this to help make progress

@amithanda amithanda self-requested a review May 17, 2026 19:04
"properties": {
"channel": {
"type": "string",
"enum": ["email", "sms", "whatsapp"],
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using an open string vocabulary instead here and at line 43 below — enum makes adding future channels (RCS, LINE, push) a schema-breaking change, which UCP tries to avoid. The spec itself says "new channels may be added as non-breaking changes," which directly contradicts using enum.

Fix:

"channel": {
  "type": "string",
  "description": "Marketing contact channel.",
  "examples": ["email", "sms", "whatsapp"]
}

@amithanda
Copy link
Copy Markdown
Contributor

amithanda commented May 17, 2026

How about this alternate design?

The protocol need this PR addresses is real and well-motivated. However, the current schema shape introduces a new pattern (two parallel arrays correlated by a shared key, sitting at different paths in the checkout) that doesn't appear elsewhere in UCP. That novel shape may cause some friction points in the schema — the closed channel enum, the buyer.complete annotation override, the unintended scope expansion onto sibling consent fields, and the need for several spec-level MUST rules to enforce consistency between the two arrays.

UCP has two established patterns for "options + decision" flows that, composed together, give a cleaner shape: the cross-actor flow direction of fulfillment_group (business offers, platform decides) plus the per-row inline state shape of payment.instruments. The composition collapses the design to a single field, eliminates each of the friction points above, and removes the need for any normative cross-array consistency rules.


How "options + decision" can be modeled in UCP

There are two established patterns. Marketing consent doesn't map cleanly to either on its own — it sits between them — but the right shape is probably a natural composition.

Pattern A — cross-actor, external ID reference (single-select)

fulfillment_group.json:

{
  "id": "group_1",
  "options": [
    { "id": "opt_standard", "title": "Standard Shipping", ... },
    { "id": "opt_express",  "title": "Express Shipping",  ... }
  ],
  "selected_option_id": "opt_express"
}

Business offers options[]. Platform selects one by stamping its ID into a sibling selected_option_id field. Right flow direction for marketing consent (business offers, platform decides), but single-select only.

Pattern B — same-actor, per-row inline state (multi-select)

payment.json + payment_instrument.json#/$defs/selected_payment_instrument:

"instruments": [
  { "id": "card_1", "type": "card", "display": {...}, "selected": true  },
  { "id": "card_2", "type": "card", "display": {...}, "selected": false }
]

One array, per-row inline selected boolean. Right shape for multi-select, but in the payment flow the platform is both the row originator (it brings its own wallet's instruments) and the decision setter — so this isn't a cross-actor example.

The marketing-consent case

Marketing consent needs Pattern A's actor flow (business offers, platform decides) and Pattern B's per-row inline state (independent yes/no per channel, with "shown and declined" distinguishable from "not shown"). Neither pattern alone fits; composing them could be a potentially cleaner option.

The proposed shape below is that composition: fulfillment_group-style cross-actor flow with selected_payment_instrument-style per-row state.


Proposed shape

// source/schemas/shopping/buyer_consent.json

"$defs": {
  "marketing_consent_option": {
    "type": "object",
    "description": "A marketing channel offered by the business. Carries the buyer's opt-in decision when sent on the complete request.",
    "required": ["channel", "display_text", "privacy_policy_url"],
    "properties": {
      "channel": {
        "type": "string",
        "description": "Marketing channel identifier. Clients MUST tolerate unknown values. Well-known: `email` (resolves to buyer.email), `sms` (resolves to buyer.phone_number), `whatsapp` (resolves to buyer.phone_number).",
        "examples": ["email", "sms", "whatsapp"]
      },
      "display_text": {
        "type": "string",
        "description": "Human-readable description of what the buyer is consenting to receive."
      },
      "privacy_policy_url": {
        "type": "string",
        "format": "uri",
        "pattern": "^https://",
        "description": "HTTPS URL of the privacy policy governing this consent."
      },
      "opted_in": {
        "type": "boolean",
        "description": "Buyer's opt-in decision for this channel. Set by the platform on the complete request. Omit in business responses.",
        "ucp_request": {
          "create": "omit",
          "update": "omit",
          "complete": "required"
        }
      }
    }
  },

  "dev.ucp.shopping.checkout": {
    "title": "Checkout with Buyer Consent",
    "allOf": [
      { "$ref": "checkout.json" },
      {
        "type": "object",
        "properties": {
          "buyer": {
            "$ref": "#/$defs/buyer",
            "ucp_request": {
              "create": "optional",
              "update": "optional",
              "complete": "omit"
            }
          },
          "marketing_consent": {
            "type": "object",
            "properties": {
              "options": {
                "type": "array",
                "items": { "$ref": "#/$defs/marketing_consent_option" }
              }
            },
            "ucp_request": {
              "create": "omit",
              "update": "omit",
              "complete": "optional"
            }
          }
        }
      }
    ]
  }
}

Example flow

Create response (business → platform):

{
  "marketing_consent": {
    "options": [
      { "channel": "email", "display_text": "Promotional emails", "privacy_policy_url": "https://example.com/privacy" },
      { "channel": "sms",   "display_text": "Order updates via text", "privacy_policy_url": "https://example.com/privacy" }
    ]
  }
}

Complete request (platform → business):

{
  "marketing_consent": {
    "options": [
      { "channel": "email", "display_text": "Promotional emails", "privacy_policy_url": "https://example.com/privacy", "opted_in": true  },
      { "channel": "sms",   "display_text": "Order updates via text", "privacy_policy_url": "https://example.com/privacy", "opted_in": false }
    ]
  }
}

The platform echoes back the options it actually displayed (with opted_in stamped on each) and omits any option it chose not to show.

What this resolves

Friction point in current PR Resolution under proposed shape
Closed channel enum needed as correlation key Removed — channel becomes a natural open string, no cross-array correlation required
buyer.ucp_request.complete: "omit" → "optional" override on base schema Removed — marketing_consent is its own top-level field with its own lifecycle
Unintended scope expansion on analytics, preferences, sale_of_data flowing on complete Removed — buyer.complete stays "omit"
Two parallel arrays at different checkout paths Collapsed into one field
Channel name duplicated across both arrays No duplication — single row per channel
Two spec MUSTs ("MUST NOT send undisplayed", "MUST ignore unsolicited") Reduced to one ("Businesses MUST ignore options not in the offered set") — bounded local check instead of cross-array consistency

What do you think?

@jamesandersen
Copy link
Copy Markdown
Contributor

Thanks @amithanda for the alternate design and rationale. A few thoughts:

Hoisting out of buyer.consent

I think the strongest argument for the single-field design is the unintentional scope expansion. The current shape requires changing buyer.ucp_request.complete from "omit" to "optional" in buyer_consent.json, which unintentionally opens the door for analytics, preferences, and sale_of_data to flow on complete — even though they aren't the target of this change. A checkout-level marketing_consent field avoids that entirely.

You also noted the buyer.ucp_request.complete: "omit" → "optional" on the schema - this is actually something I was trying to better understand before submitting my own PR (e.g. how the UCP tooling would handle this) when I saw @wsbrunson 's PR come in. We may be able to side-step this rabbit hole for now with alternate design (though I'm curious if we'll have to deal with this at some point down the road).

The trade-off is that consent is no longer in one place e.g. marketing consent and buyer.consent which feels awkward ... but see "Broader Consent Model" heading below.

Modeling the Marketing option

Contact Source

One addition worth considering: making the channel-to-contact-field mapping explicit in the schema rather than only in the spec prose. Something like:

{                                                                                                                          
  "channel": "email",
  "contact_path": "$.buyer.email",                                                                                         
  "display_text": "Promotional emails",                                                                                    
  "privacy_policy_url": "https://example.com/privacy"                                                                      
}                                                                                                                          

UCP already uses RFC 9535 JSONPath in discount.json (allocation targets) and the message types (component references). Those are referential (pointing at a related component for context), not functional (resolving a value) — so contact_path would be a new kind of usage. But it makes the mapping machine-readable and avoids the problem of adding a new channel in the future whose contact source isn't obvious from convention alone.

This could be business-provided (they specify where the contact info lives) or spec-defined (well-known channels have documented paths). Either way, it's more robust than prose-only documentation.

Opt-in vs opt-out ambiguity

The current marketing: true from a US platform (hasn't opted out) means something very different legally than marketing: true from an EU platform (affirmatively consented). Should this be accounted for in our modeling of opted_in? e.g. should we capture if the business intends to model this as opt-in vs. opt-out (and platform thus renders a default unchecked vs. checked UX)?

Broader consent model

Before we converge on a shape, do we want to think more broadly about the other consent options at play? e.g. each one of these appears to be a nod to some regulatory consideration e.g. analytics -> GDPR Art. 6(1), sale_of_data -> CCPA / CPRA, preferences -> ePD Art. 5(3). UCP is a protocol and doesn't waive either Platform or Business from regulatory compliance so I think we need to be careful in all of these consent scenarios. E.g. protocol can model what the platform needs to present the business's consent experience but, as a protocol, and doesn't waive either Platform or Business from their respective regulatory compliance obligations

@igrigorik you also signaled some interest in sharing thoughts on this one in the last TC meeting. If that's still the case, it might be worth hearing that perspective before we iterate further on schema details here — to avoid shipping a narrow solution that gets immediately superseded.

Concretely: are we solving just marketing consent now (in which case the alternate design above may be getting us close to what we need), or is there appetite for a unified consent redesign that would subsume this PR? Happy to go either direction — just want to make sure the group is aligned on scope.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

TC review Ready for TC review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants