Technical guide

Integrating with California's DROP API

What a data broker's engineering team actually has to build to satisfy the Delete Act's deletion cycle: three endpoints, two file formats, four status codes, and a clock that never stops.

The shape of the obligation

Since August 1, 2026, every registered California data broker must access DROP at least once every 45 calendar days, retrieve the consumer deletion lists it has selected, match them against its own records, delete what matches, and report a status for every request. Failure to process carries $200 per deletion request, per day.

Integration via API is optional — the full cycle can be completed manually through the portal — but the file formats and matching rules are identical either way.

Authentication and environments

Keys are issued in the Data Broker Portal after you select your consumer deletion lists. A separate sandbox key is issued for testing. Every request carries the key in a header; issuing a new key deactivates the old one.

GET https://api.drop.privacy.ca.gov/data/download
X-API-KEY: <your key>
Accept: application/zip, application/json

# sandbox: https://api.drop.privacy.ca.gov/sandbox/data/download

Note the account model: DROP supports a single login per broker, and the account must belong to the broker itself — not to a vendor in its own capacity. A service provider operates as the broker's authorized agent.

The three endpoints

EndpointPurposeNotes
GET /data/downloadRetrieve the deletion listsReturns a ZIP, or 202 + Retry-After while the package builds. 409 means the previous batch's responses are incomplete.
POST /data/uploadReport statusesmultipart files; CSVs only, never a ZIP. 202 = queued; row-level validation continues after the response.
POST /data/amendCorrect a submitted statusIdentical request shape to upload. Needed when a "not found" consumer later appears in newly collected data.

File formats — the casing matters

The download archive contains one CSV per selected list type, plus a Removed file when previously delivered identifiers are withdrawn. Header casing differs between download and upload, and the upload header is validated strictly.

20260801_4821_DROP.zip
├── 20260801_4821_Email.csv     ID,Hash
├── 20260801_4821_NDZ.csv       ID,Hash
└── 20260801_4821_Removed.csv   ID,Hash,ListType

# your response file — note "Id", not "ID"
Id,Status
A7kP2xQ9Lm4R,3
B2mN8vR4Kp7Q,5

File names follow YYYYMMDD_<brokerId>_<DataType>[_<suffix>].csv. Uploading two files with the same name inside one download is rejected, so split large responses with the optional suffix (_part01, _part02).

Status codes

CodeMeaningWhen
2ExemptedMatch found, but all the personal information is exempt under the CCPA. Self-asserted — audited later.
3DeletedMatch found, non-exempt information deleted — including inferences, archives, and backups.
4Opted outMultiple consumers share the identifier; all were opted out of sale and sharing instead.
5Not foundNo match — but you must retain the identifier and screen future data against it.

Codes 0 and 1 are unused.

Webhooks

Register an HTTPS endpoint in the portal and DROP will notify you when a download is ready or an upload is processed. Signature verification is easy to get subtly wrong — the signing string is the timestamp, a literal dot, then the raw body, and the digest is hex, not Base64.

const expected = hmacSha256Hex(secret, `${timestamp}.${rawBody}`);
const received = header["X-Webhook-Signature"].replace("sha256=", "");
if (!constantTimeEqual(expected, received)) reject();
// also reject timestamps older than ~5 minutes; respond 204

The cycle clock

Downloads are incremental: after the first, each one returns only requests received since your last completed download. The rules that trip teams up:

  • Access at least once every 45 calendar days — this is a rolling maximum interval, not a fixed window.
  • Report the previous session's statuses before downloading a new list.
  • You may change your list selection only once every 45 days.
  • Retry policy: honour Retry-After on 429 (default 30s) and poll on 202; do not retry other 4xx.
The matching itself is the part with no margin for error — normalization and hashing must match the specification exactly, or you will silently under-delete. That's covered in the hashing guide, with the official test vectors.

Or skip the build

PrivacyClock implements all of this — validated against every published test vector — for $299/mo.

Try it on sample data →