Consent · engineers · PMs
GPP header type is 3
GPP is one string with sections joined by tildes. The header is not optional decoration. Type 3 is GPP. Type 2 is a TC String you put in the wrong parameter.
Header type 3, version 1
gpp is URL-safe base64 with a tilde between sections. The first section is the header. Its first six bits are fixed to 3 as the GPP Header field. The next six bits are the specification version, currently 1. A well-formed v1 header often starts with the letters DB. Anything that only checks characters will accept a lot of strings that are not GPP.
Pixellint decodes those bits. A header type that is not 3 is core.privacy.gpp_header_type. A published version that is not 1 is core.privacy.gpp_header_version. Characters that are not URL-safe base64 with tildes are core.privacy.gpp_malformed. Empty gpp and an unexpanded ${GPP} are template slots. Core privacy rules skip those, the same as TCF.
https://example.com/px?id=1&gpp=DBACNYA~CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA~1YNN&gpp_sid=2
A TC String in gpp is type 2
Pasting a TC String into gpp is the common mix-up after a CMP upgrade. The first six bits of a TCF v2 string are the version, which is 2, so the GPP decoder reports header type 2. The string itself may be a perfect TC String. It still belongs in gdpr_consent, not here.
One CMP method, two parameters, two encodings. Copying the TC String into both slots looks thorough in a screenshot and fails the GPP spec on the wire. Fix the CMP mapping, not the alphabet. pixellint validate url will name core.privacy.gpp_header_type. It will not try to be helpful by reading the TC String out of the wrong key.
https://example.com/px?id=1&gpp=CPXxRfAPXxRfAAfKABENB-CgAAAAAAAAAAYgAAAAAAAA&gpp_sid=2
gpp_sid shape
gpp_sid carries the section ID or IDs in force for this transaction. Normally one integer, at most two separated by a comma. Missing sid with a populated gpp string is core.privacy.gpp_sid_missing. A list, a hyphen range, or the word us is core.privacy.gpp_sid_malformed. usnat, TCF, and 2-7 are not ids the parameter accepts.
sid is which section applies here, not a table of contents. During a US Privacy migration you may send gpp alongside us_privacy. That is two generations of US signal, not a duplicate of the same key. Two gpp_sid query keys is a duplicate. One gpp_sid with two comma-separated ids is the documented way to name at most two sections in force.
https://example.com/px?id=1&gpp=DBACNYA&gpp_sid=usnat
sid does not have to match the string
The GPP spec does not require sid to match every section inside the string, and Pixellint does not invent that check. A string can contain more sections than the ones in force for this hop. sid says which section the callee should apply now. Cross-checking the header's section list against sid would be a different product's rule.
If you want that extra check, do it in your own QA, not by reading a Pixellint finding that does not exist. Core will still fail a missing sid, a malformed sid, a bad header type, and a duplicate gpp or gpp_sid key. That is the contract. Do not file a bug because sid=2 sits next to a string that also encodes other sections.
Macros, empties, and cutover
A fired URL still holding ${GPP} never sent GPP. Empty gpp= is an unfilled template slot. Core privacy rules skip those so a creative is not scored as malformed-string. After serve, the live request has to carry a decoded header type 3 and a sid.
Sending us_privacy and GPP together during a cutover is fine. Dropping us_privacy before every partner reads gpp_sid will look like you went silent in California. Keep the old string until the bid path and the pixel path both consume GPP. Then drop us_privacy on purpose, not by deleting the wrong macro.
https://example.com/px?id=1&gpp=${GPP}&gpp_sid=${GPP_SID}&us_privacy=${US_PRIVACY}
Check the artifact
Paste the pixel URL or JSON body into the
playground. Same engine as
pixellint validate. Nothing leaves the tab.