Kentro Pixel limits, CORS behavior, reason codes, request errors, troubleshooting, background processing and retention.
This page collects the numbers and codes you need when something does not arrive: request and rate limits, CORS, per-event reason codes and request errors. It also explains what happens after ingestion and how long each kind of data is kept.
In this page
- Limits
- CORS
- Per-event reason codes
- Request errors
- Troubleshooting
- Processing
- Retention
Limits
| Limit | Value |
|---|---|
Browser request body (/v1/events, /v1/events/batch) | 64 KB |
Server request body (/v1/server/events) | 512 KB |
| One event, serialized | 32 KB |
| Events per request | 100 on browser routes and the validate endpoint, 500 on the server route |
GIF beacon e parameter | 16 KB of base64url |
occurred_at skew | 7 days in the past, 5 minutes in the future |
| Server signature window | 300 seconds |
| Idempotency key | 128 characters |
| Rate limit, public key | 600 events per minute, bursts up to 1,200 |
| Rate limit, server key | 6,000 events per minute, bursts up to 12,000 |
| Rate limit, client IP on browser routes and the validate endpoint | 600 requests per minute, bursts up to 1,200 |
| Rate limit, client IP on the server route | 1,200 requests per minute, bursts up to 2,400 |
| Lookups of keys not seen in the last minute, per client IP | 60 per minute, bursts up to 120 |
Signup capture (SDK form) | 60 signups per minute per property; body up to 4 KB |
| Identity tokens | 5 distinct tokens per request, 1,024 characters each, valid at most 24 hours |
Duplicate detection (event_id) | At least 14 days per property |
| Allowed origins | 50 per property |
| Sensitive path patterns | 50 per property, 200 characters and 20 segments each |
| Custom event schemas | 500 per workspace are loaded |
| Destination event mapping | 50 entries |
| Destination replay window | At most 180 days long, from at most 30 days back |
| Privacy request subject keys | 1 to 100 |
Key rate limits count events, not requests (a batch of 50 uses 50); client IP limits count requests, and lookups of unknown keys count separately, so unknown keys cannot be used to scan for valid ones. All limits are enforced per collector instance. A refused request answers 429 RATE_LIMITED with a Retry-After header in seconds; the SDK retries automatically.
CORS
- The collector endpoints answer CORS themselves:
Access-Control-Allow-Originechoes the request origin when the property allows it (or*), withVary: Origin,Timing-Allow-Origin, methodsPOST, GET, OPTIONS, headersContent-Type, X-Kentro-Pixel-Keyand a preflight cache of 10 minutes (Access-Control-Max-Age: 600). Credentials are not used. - The SDK sends
text/plainwith the key in the body, which needs no preflight. Sendingapplication/jsonor theX-Kentro-Pixel-Keyheader from a browser triggers a preflight, which is supported. - The SDK file is served with
Access-Control-Allow-Origin: *andCross-Origin-Resource-Policy: cross-origin. - The signup capture used by
formechoes only an allowed origin and never uses credentials. - The management API uses the standard Kentro API CORS policy.
Per-event reason codes
| Code | Status | Meaning |
|---|---|---|
INVALID_ENVELOPE | rejected | Not an object, fails the envelope rules (types, lengths, the event_name pattern) or occurred_at is not a date. |
PAYLOAD_TOO_LARGE | rejected | The event is larger than 32 KB serialized. |
CLOCK_SKEW | rejected | occurred_at is more than 7 days in the past or 5 minutes in the future. |
UNKNOWN_EVENT | rejected | No builtin or custom schema for this event_name and schema_version. |
RESERVED_EVENT | rejected | The name is one Kentro produces itself, refused on every route and for any schema_version. The reason says what to use instead. |
SCHEMA_VIOLATION | rejected | properties does not match the schema; reason points at the field. |
SENSITIVE_PAYLOAD | quarantined | Health terms, ICD-10 codes, secrets or card numbers were detected. |
SENSITIVE_SCAN_INCOMPLETE | quarantined | The payload was too large or too deeply nested to scan completely. |
CONSENT_DENIED | suppressed | The effective consent for the event's purpose is denied. |
GPC_OPT_OUT | suppressed | Global Privacy Control alone denied an event with the marketing purpose. |
SENSITIVE_REDACTED | accepted | Authoritative server commerce events only: matching fields were replaced; the order still counts, but the event is never forwarded to destinations. |
IDENTITY_UNVERIFIED | accepted | Browser events: a person reference without a valid identity token was dropped; the event was recorded anonymously. |
Request errors
Request-level errors refuse the whole request. Handler errors answer { "error": "<CODE>", "reason_code": "<CODE>", "message": "..." }; framework errors (400 for unparsable JSON, 413, 415, 500) answer { "error": "<CODE>", "message": "..." }. The same codes apply to browser and server routes.
| HTTP | Code | When | Retry |
|---|---|---|---|
| 400 | INVALID_ENVELOPE | The body is not JSON, not an envelope or { "events": [...] }, or has no events. | No |
| 400 | BATCH_TOO_LARGE | More than 100 (browser) or 500 (server) events; the body carries max. | Split the batch |
| 401 | KEY_INVALID | Missing or unknown key, disabled property, or a key whose environment does not match the property. | No |
| 401 | KEY_REVOKED | The key was revoked. | No |
| 401 | SIGNATURE_INVALID | Missing or wrong signature, or the body was not sent as application/json. | After fixing the signing |
| 401 | TIMESTAMP_STALE | X-Kentro-Timestamp missing, not digits, or more than 300 seconds off. | After re-signing |
| 403 | ORIGIN_NOT_ALLOWED | The browser origin is not in allowed_origins. | No |
| 409 | IDEMPOTENCY_KEY_REUSED | Server route: the idempotency key was already used with a different body. | With a new key |
| 413 | PAYLOAD_TOO_LARGE | The body exceeds 64 KB (browser) or 512 KB (server). | Split the batch |
| 415 | UNSUPPORTED_MEDIA_TYPE | A content type other than application/json (or text/plain on browser routes). | No |
| 429 | RATE_LIMITED | Too many events for this key, or too many requests (or unknown keys) from this client. | After Retry-After |
| 500 | INTERNAL_ERROR | Unexpected error. | With backoff |
| 503 | COLLECTOR_DISABLED | The collector is switched off. | Later |
| 503 | INGEST_UNAVAILABLE | Storage is temporarily unavailable. | With backoff |
| 503 | SIGNING_UNAVAILABLE | Server key secrets are temporarily unavailable. | Later |
Troubleshooting
| Symptom | Likely cause | What to do |
|---|---|---|
| No request leaves the page | The script tag has no data-key, or init was called without key and endpoint; or a call was queued before init. | Use the snippet from GET /v1/properties/:id/snippet, or pass both options to init, and queue init first. |
| Requests blocked by the browser | A Content-Security-Policy without the collector host. | Allow https://pixel.kentro.live in script-src and connect-src (and img-src for the GIF fallback), and allow the inline queue stub with a nonce or hash. |
401 KEY_INVALID | Wrong key, disabled property, or a pk_test_ key on a production property (or the reverse). | Copy the key from the property; keys are tied to the property's environment. |
401 KEY_REVOKED | The key was revoked. | Create a new key and update the snippet. |
403 ORIGIN_NOT_ALLOWED | The page's host is not in allowed_origins, or the request carried neither Origin nor Referer while an allowlist is set. | Add the exact host or a *.domain entry (the apex needs its own entry). A Referrer-Policy: no-referrer page cannot use the GIF beacon with an allowlist. |
Result rejected, UNKNOWN_EVENT | The name is not in the taxonomy, is not lower-case namespace.name, or schema_version does not match. | Use a name from the taxonomy. |
Result rejected, RESERVED_EVENT | The name is one Kentro produces itself: attribution, audience membership, conversions, Kentro GO campaign data or Kentro Email history (email.*). | Stop sending it; use what reason names. |
Result rejected, SCHEMA_VIOLATION | A property is not on the event's allowlist, a required property is missing, or money was sent as a float. | Check reason; send money as { "amount_minor", "currency" }; put identifiers under properties.customer. |
Result rejected, CLOCK_SKEW | occurred_at is far off, or was sent in milliseconds. | Send ISO 8601 timestamps from a synchronized clock. |
Result suppressed, CONSENT_DENIED or GPC_OPT_OUT | Consent is unknown or denied for the event's purpose, or GPC alone blocked a marketing event. | Report consent with consentChanged; include consent in server envelopes; review settings.consent. |
Result quarantined, SENSITIVE_PAYLOAD | A property value, the page title or a form value matched a detector, for example "appointment" or "patient" in a page title. | Keep medical terms, secrets and card numbers out of event data and page titles. |
Result duplicate | The event_id was already accepted for the property, or an envelope without event_id was identical to an earlier one. | Give every real-world event its own event_id; expect duplicate on retries. |
429 RATE_LIMITED | More than the per-key budget. | Wait for Retry-After; batch events; use a server key for high-volume backend traffic. |
401 SIGNATURE_INVALID on the server route | The signed string differs from the bytes sent, the secret belongs to another key id, the sha256= prefix or lower-case hex is missing, the secret was base64-decoded, or the body was not sent as application/json. | Sign the exact string you send, with the secret as given, and send Content-Type: application/json. |
401 TIMESTAMP_STALE | The clock is off by more than 300 seconds, or the timestamp is in milliseconds. | Send Unix seconds from a synchronized clock. |
Result accepted with IDENTITY_UNVERIFIED | A person reference without a valid identity token (missing, malformed, expired, or signed by a revoked key or a key of another property). | Mint tokens as in Identity tokens; never put subject_ref in browser events. |
409 IDEMPOTENCY_KEY_REUSED | The idempotency key was already used with a different body. | Use a new key for every new batch. |
503 COLLECTOR_DISABLED, INGEST_UNAVAILABLE, SIGNING_UNAVAILABLE | The collector is switched off, or storage or secrets are temporarily unavailable. | Retry later with the same event_id values. |
Signups from the form command never arrive | The property's allowed_origins is empty or misses the page's host (403), the consent checkbox was not checked, the email field was not found, or a Content-Security-Policy blocks https://t.kentro.email. | Add the host to allowed_origins, check consentSelector and emailField, and allow the host in connect-src. Set debug: true to see option warnings. |
| Events in the debugger but not in analytics or a destination | Processing runs in the background; a destination sends only mapped events once it is enabled, and only with the consent it needs. | Check processing_status in the debugger, the destination's status and mapping, and its delivery history (suppressedReason). |
404 Not Found on every management route | The management API is switched off on this environment. | During the preview it is enabled per environment; contact [email protected] for access. |
Processing
After ingestion, Kentro's background processor works through accepted events in the order they arrived:
- People and sessions. Each event is attached to the person keys it carries; a verified login or an order links an anonymous visitor to the person. Sessions follow
settings.session(30 idle minutes by default) and record the touchpoint: UTM values, hashed ad click ids, the campaign reference and the referrer class. Sessions, touchpoints and browser conversions need analytics or marketing consent. - Kentro Email touchpoints. A click on a tracked Kentro Email link records a touchpoint of channel
email(sourcekentro_email, mediumemail) for that contact, so attribution can credit email. Opens are not touchpoints. - Consent record. Explicit consent choices are filed as described in Consent and privacy.
- Conversions.
commerce.order_placedandcommerce.order_paidrecord an order, and cancellations and refunds correct it;marketing.lead_captured,marketing.signup_completedandmarketing.demo_bookedare lead conversions. Each order counts once (a server copy wins over a browser copy,commerce.order_paidovercommerce.order_placed) and each lead once per conversion type andlead_ref. - Destinations. Each mapped event of an enabled destination becomes a delivery, sent with retries; consent is checked again when it is sent.
- Attribution. Last-touch attribution over 30 days is refreshed automatically when conversions change; the other models run on request (
POST /v1/analytics/attribution/runs). - Audiences. Active audiences are recomputed about every 10 minutes; the membership changes are what an audience sync sends.
- Privacy requests run as described in Consent and privacy.
Retention
Retention is enforced automatically. Changing settings.retention applies to the history already stored: a longer period keeps existing history longer, and a shorter one removes stored history past the new period, starting with the next daily retention run.
| Data | Kept |
|---|---|
| Event history | At least 13 months, or longer when settings.retention.journalMonths asks for it |
| Ingestion receipts and server batch idempotency keys | As long as the property's event history |
| Accepted event ids, for duplicate detection | 14 days |
| Rejected-event samples (ingestion health) | settings.retention.errorsDays: 30 days by default, 1 to 365 |
| Attribution results and order reconciliation records | As long as the property's event history |
| People records (person keys and the links between them) | Removed once a person has not been seen for the full event retention period, belongs to no active audience and has no conversion within that period. Kentro Email contacts, and people a Kentro Email contact's customer record points to through an authorized CRM link, are kept. Deletion records of privacy requests are kept, so a deleted person is never recreated. |
| Consent records | As long as the person's record, and at least for the event retention period. Consent withdrawal records are always kept. |
| Audience membership history | Current memberships while they last; a leave is kept 90 days |
| Raw ad click ids | 90 days, then removed from sessions and events |
| The full browser user agent of marketing-consented events | 30 days |
| The salted hash of the client IP address on ingestion receipts (the raw address is never stored) | 30 days |
| Delivery history | 30 days; a delivery still waiting to be sent is kept until it is sent or gives up |
| Privacy request records | 24 months after completion, then deleted |
| Privacy export downloads | 24 hours |

