CCPA § 1798.120(c) and § 1798.135(b) require opt-out requests to be processed within 15 business days of receipt. For real-time client-side opt-outs — a cookie is set and pixels stop firing immediately — this deadline is automatically satisfied. The gap is backend-processing opt-outs: requests that require staff to remove the consumer from a CRM, advertising platform audience, or data broker list. Without a tracking system and documented SLA, there is no way to prove compliance, and regulators treat the absence of tracking as evidence of non-compliance. An opt-out form that routes to an unmonitored inbox with no SLA is a CCPA violation waiting for an enforcement trigger.
Medium because the 15-business-day processing requirement under CCPA § 1798.120(c) is specific and auditable — a manual fulfillment queue with no SLA tracking is a documented non-compliance gap.
For real-time cookie-based opt-outs, document that the 15-day deadline is satisfied automatically. For any backend processing (removing from CRM lists, third-party advertising audiences), persist an opt-out record with a timestamp and send the consumer a confirmation email.
// app/api/privacy/opt-out/route.ts — log, confirm, and schedule third-party removal
export async function POST(req: Request) {
const { email } = await req.json()
await db.privacyPreference.upsert({
where: { email },
create: { email, optedOutAt: new Date(), type: 'sale_sharing' },
update: { optedOutAt: new Date() },
})
await sendEmail({
to: email,
subject: 'Your opt-out has been processed',
body: 'Effective immediately. Third-party advertising lists updated within 15 business days.',
})
await notifyThirdPartiesOfOptOut(email)
return Response.json({ ok: true, processedAt: new Date().toISOString() })
}
Track third-party list removals in PRIVACY_OPERATIONS.md so you can demonstrate the 15-business-day SLA was met if a regulator asks.
ID: ccpa-readiness.opt-out.opt-out-processing-time
Severity: medium
What to look for: CCPA requires opt-out requests to be processed within 15 business days of receipt. Check whether opt-out requests are processed immediately (in real-time via the opt-out mechanism) or queued for later processing. If queued, check the queue mechanism and whether a 15-business-day SLA is tracked. For real-time opt-outs (user clicks a button and a cookie is set + sharing immediately stops), the 15-day requirement is automatically satisfied. For backend-processed requests (e.g., requests to remove a consumer's data from a CRM or third-party platform after a form submission), check whether there is an internal tracking system, SLA documentation, or workflow that ensures fulfillment within 15 business days. Check whether opt-out confirmation is sent to the consumer. Count all instances found and enumerate each.
Pass criteria: Opt-out of sale/sharing takes effect immediately when the user interacts with the opt-out mechanism (real-time). For any opt-out actions that require backend processing (removal from third-party lists, CRM updates), a documented process ensures completion within 15 business days and the consumer receives confirmation. Threshold: no more than 15 business days.
Fail criteria: Opt-out form submission has no documented processing SLA. Opt-out mechanism is "request-based" with no confirmation or tracking. Third-party data sharing is not stopped in real-time — only queued for future processing without a guaranteed timeline.
Skip (N/A) when: Application does not sell or share personal information — document.
Detail on fail: Example: "Opt-out form submits a request to a manual inbox with no documented SLA. No confirmation email sent to consumer. No tracking of fulfillment timing." or "Opt-out cookie set client-side immediately, but Facebook Pixel firing is not gated on the cookie on first load — sharing may occur before the cookie is read.".
Remediation: For real-time client-side opt-outs, document that the 15-day requirement is satisfied automatically. For any backend processing, implement tracking:
// app/api/privacy/opt-out/route.ts — log and confirm opt-out
export async function POST(req: Request) {
const { email } = await req.json()
// Store opt-out preference
await db.privacyPreference.upsert({
where: { email },
create: { email, optedOutAt: new Date(), type: 'sale_sharing' },
update: { optedOutAt: new Date() },
})
// Send confirmation email
await sendEmail({
to: email,
subject: 'Your opt-out request has been processed',
body: [
'Your request to opt out of the sale or sharing of your personal information',
'has been processed and is effective immediately.',
'',
'If your information was previously shared with third-party advertising partners,',
'we will notify those parties of your opt-out within 15 business days.',
].join('\n'),
})
// Schedule third-party notification if applicable
await notifyThirdPartiesOfOptOut(email)
return Response.json({ ok: true, processedAt: new Date().toISOString() })
}