Consent Docs

Key and API errors

Fix the errors the API answers with most often, from 401 and missing scopes to rate limits, refused answers and keys that cannot go live.

Troubleshooting6 min readFor anyone with something not working

Find the error by its status or its code. Read the HTTP status first, then code in the body. Match on the code, not on the wording of the message.

401 Unauthorized#

What you see: A call answers 401 with the message Unauthorized and no code.

Why it happens: The key was not accepted. The answer is the same for each cause:

  • The key is missing, wrong, revoked or turned off.
  • A publishable key was used from an origin or app id that is not on its list.
  • A live key was used from localhost.
  • A publishable key called an endpoint that takes only a secret key.

Fix: Find the key in Install. The line under its name says where it works. From a server, send Authorization: Bearer sk_…. A key's origins cannot be edited, so create a new key to add one. See Authentication.

SCOPE_REQUIRED#

What you see: A call on a person, such as find, export, withdraw or erase, answers 403 with the code SCOPE_REQUIRED.

Why it happens: The secret key lacks the scope the call needs. Every secret key holds the two consent scopes. The scopes for calls on a person are off until you grant them. A publishable key never has them.

Fix: In Install, choose Scopes on the key, tick what that server needs and choose Save scopes. See Manage keys.

SECRET_KEY_IN_BROWSER#

What you see: A call answers 403 with the code SECRET_KEY_IN_BROWSER. The key shows an Exposed badge in Install.

Why it happens: A secret key arrived with a browser's Origin or Sec-Fetch-Site header. Anyone who can read the page may now have the key. The badge does not stop the key: it still works from your server.

Fix: Create a new key, deploy it on your server, then choose Revoke on the exposed key. Move the call to your server. See Keys and where to use them.

429 Too many requests#

What you see: Calls answer 429 with a Retry-After header.

Why it happens: A budget for the minute is used up:

Budget Default
All requests with one key 600 a minute for a test key, 6,000 once it is live
Writes with a publishable key from one address 60 a minute
Calls on a person with one key 60 a minute
Identifiers checked with one key 600 a minute

Fix: Wait for the seconds in Retry-After, then continue. For a large import, use Import CSV in Records, or stay under ten writes a second with a test key. The browser runtime sends a refused write again on a later page load, so the answer is not lost. See Rate limits.

CHECK_BUDGET_EXCEEDED#

What you see: POST /consent/check answers 429 with the code CHECK_BUDGET_EXCEEDED and a Retry-After header. The SDK helpers return unavailable with the cause rate_limited.

Why it happens: The key checked its limit of identifiers this minute: 600 unless you set Checks per minute. Each identifier counts, the subject and each alsoKnownAs entry.

Fix: Raise Checks per minute on the key, up to 6,000, or send fewer alsoKnownAs entries. Whoever runs the deployment can raise the 6,000 with SDK_CHECK_MAX_PER_MINUTE. The first refusal in a minute is written to the audit log. See Consent check.

What you see: The consent check answers no_record for a person you know consented.

Why it happens: No record is held under the identifier you checked, or linked to it. The usual causes are a different identifier type, a phone number written another way, a test key reading live records, or consent held only under a cookie id.

Fix: Write phone numbers in E.164, such as +919846011223, as when you recorded them. Add the person's other identifiers in alsoKnownAs, or link them to the record. Use a live key for live records. See How a consent check decides.

What you see: A write answers 400 with the code CONSENT_FORM_NOT_PUBLISHED.

Why it happens: The form has no published version, so consent against it could not be proven.

Fix: Publish the form, then send the request again. If the request carried an Idempotency-Key, use a new key, or the stored refusal comes back. See Publish a version.

INVALID_PURPOSE_DECISIONS#

What you see: A write answers 400 with the code INVALID_PURPOSE_DECISIONS or INVALID_PARTNER_DECISIONS.

Why it happens: The answers do not match the published form. A purpose on the form was not answered, or an answer names a purpose or partner the form does not offer.

Fix: Read errorData in the body. missingPurposeKeys, unknownPurposeKeys and unknownPartners name what is wrong. Answer every purpose that is not always on. See Record consent from your server.

IDENTITY_PROOF_REQUIRED#

What you see: A browser or an app answers 403 with the code IDENTITY_PROOF_REQUIRED or IDENTIFIER_PROOF_REQUIRED.

Why it happens: The page tried to record or link an email address, phone number or user id without a valid proof. A proof also stops working the moment the identity secret is rotated.

Fix: Compute the proof on your server for exactly that type and identifier, with the key's current idv_ secret. See Record consent for signed-in visitors.

IDEMPOTENCY_KEY_REUSED#

What you see: A write answers 422 with the code IDEMPOTENCY_KEY_REUSED. Or a request you have fixed keeps getting the old refusal.

Why it happens: An Idempotency-Key is tied to one request for 24 hours. A different body with the same key is refused. Any answer below 500 is kept and replayed, including a 400.

Fix: After a refusal, fix the cause and send the request with a new key. Reuse a key only to repeat the same request. A 409 with IDEMPOTENCY_REQUEST_IN_PROGRESS means the first request is still running: wait a second or two and repeat it. See Conventions.

What you see: An update, a withdrawal or a receipt answers 404 with the code CONSENT_RECORD_NOT_FOUND, for an id you know is right.

Why it happens: An update or a withdrawal reaches only records of the key's own kind. A test key cannot change a live record, and a live key cannot change a test record.

Fix: Check which key the call uses. After a key goes live, run PiedapConsent.reset() once in the browsers you tested with. They still hold the id of a test record. See Test your integration.

500 on a large request#

What you see: A request answers 500 with the message Internal server error, and a retry fails the same way.

Why it happens: The JSON body is over 100 KB. The API answers that way before it handles the request. A web server in front of the API may answer 413 instead.

Fix: Send a smaller body. metadata may hold up to 8,192 bytes of JSON, and one request up to 100 decisions. See Errors.

Request to go live is refused#

What you see: Request to go live is refused, or the key shows Declined.

Why it happens: A key needs somewhere to run and something to serve:

  • SDK_KEY_NEEDS_ORIGIN: the key has no allowed origin, no app: id and no secret key.
  • SDK_KEY_NEEDS_PUBLISHED_FORM: its project or property has no published form.
  • Declined: the reviewer turned the request down. The reason is under the key's name.

Fix: Publish a form. If the key lacks its production origins, create a new key that lists them. After a decline, fix what the reason names and ask again. See Request to go live.

Still stuck#

Collect these before you ask for help:

  • the HTTP status, the code and the message
  • the X-Request-Id header of the response
  • the method and path of the call, and the time
  • the key's hint, such as pk_…Nimw, never the key itself

↑↓ to move, Enter to open