REVU Behavior Ingest API#
The single endpoint every REVU SDK targets to push captured events.
REVU SDKs capture behavioral events client-side, batch them, and POST them to this endpoint. The SDK only captures; the server validates, attributes, and stores. Authentication is a public, browser-embeddable write key (revu_pk_...) carried in the request body, not a secret header: it is a tenant identifier that grants only append access to one organization's event log. Ingest is idempotent on (organization, event_id), so retried batches are de-duplicated server-side.
This page is generated from the OpenAPI specification. Download it as openapi.json to drive a client generator, a mock server, or an AI agent integration.
Servers#
https://api.revu.ai- Production ingesthttps://{host}- First-party ingest through your own domain (reverse proxy). See the web SDK first-party-ingest guide.
Authentication#
Authentication is a public write key (revu_pk_...) sent in the request
body as api_key, not an Authorization header. The key is a tenant
identifier safe to embed in client bundles; it grants only append access to
one organization's event log. There are no cookies and no session.
POST /v1/behavior/events#
Ingest a batch of behavioral events
Accept a batch of captured events from a REVU SDK. The body carries the public api_key and a batch of 1 to 200 events. Returns 204 with an empty body on success. The SDK sends Content-Type: application/json; on page unload it uses navigator.sendBeacon with an application/json Blob so the terminal batch still parses.
Request body#
Content type: application/json
IngestBody#
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
api_key | string | yes | pattern: ^revu_pk_[A-Za-z0-9_-]{8,120}$ | Public ingest write key (prefix revu_pk_). |
batch | array<BehaviorEvent> | yes | items 1-200 | Up to 200 events per request. |
BehaviorEvent#
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
event_id | string | yes | format: uuid | Client-generated UUID. Unique per organization; the idempotency / dedupe key for retried batches. |
anonymous_id | string | yes | length 1-128 | First-party anonymous visitor (device) id assigned by the SDK. |
user_id | string | null | no | length 0-128 | Identified user id, if any. Null when the visitor is not yet identified. |
session_id | string | yes | length 1-128 | Per-session id. |
sequence_no | integer | yes | min: 0 | Per-page-load monotonic counter starting at 0. Gaps indicate dropped events within a page load. |
platform | enum | yes | one of: web, ios, android | Capture platform. |
event_type | string | yes | length 1-120 | Event type: '$pageview', '$autocapture', or a custom name. |
screen | string | yes | length 0-2048 | Route / path at capture time. |
fingerprint | object | null | no | Element descriptor, present for $autocapture interactions. Used server-side to name the interacted element. | |
properties | object | yes | Event payload: capture-layer fields (path, url, depth_percent, form structure, ...) plus caller-supplied custom properties. Client-side masked, PII-free, never input values. | |
context | object | no | Engine environment bucket (unprefixed): user_agent, language, timezone, screen_ / viewport_, online, connection_*, environment, sdk_version, consent, gpc, sample_rate, and first/last-touch attribution. Optional: a client that omits it still validates. | |
device_time | string | yes | format: date-time | ISO-8601 capture timestamp from the client device. |
BehaviorEvent.fingerprint#
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
tag | string | yes | Element tag, e.g. 'button'. | |
text | string | null | no | Visible text (truncated; masked if sensitive). | |
role | string | null | no | ARIA role / type. | |
id | string | null | no | Element id, if present. | |
classes | array | null | no | Class list. | |
selector | string | yes | Best-effort CSS selector (fragile; a tiebreaker). | |
ordinal | number | no | Position among siblings. |
Example requests#
A curl call (the SDK does this for you, batched and retried):
curl -X POST https://api.revu.ai/v1/behavior/events \
-H "Content-Type: application/json" \
-d '{"api_key":"revu_pk_example12345678","batch":[{"event_id":"f1d2c3b4-a5e6-4789-9abc-de0123456789","anonymous_id":"a0e1d2c3-b4a5-4677-8899-aabbccddeeff","user_id":null,"session_id":"11112222-3333-4444-5555-666677778888","sequence_no":0,"platform":"web","event_type":"$pageview","screen":"/pricing","properties":{"path":"/pricing","url":"https://acme.com/pricing"},"context":{"user_agent":"Mozilla/5.0 ...","language":"en-US","timezone":"Europe/Berlin","environment":"production","sdk_version":"0.1.0"},"device_time":"2026-06-21T18:04:05.123Z"}]}'
A single $pageview event#
{
"api_key": "revu_pk_example12345678",
"batch": [
{
"event_id": "f1d2c3b4-a5e6-4789-9abc-de0123456789",
"anonymous_id": "a0e1d2c3-b4a5-4677-8899-aabbccddeeff",
"user_id": null,
"session_id": "11112222-3333-4444-5555-666677778888",
"sequence_no": 0,
"platform": "web",
"event_type": "$pageview",
"screen": "/pricing",
"properties": {
"path": "/pricing",
"url": "https://acme.com/pricing"
},
"context": {
"user_agent": "Mozilla/5.0 ...",
"language": "en-US",
"timezone": "Europe/Berlin",
"environment": "production",
"sdk_version": "0.1.0"
},
"device_time": "2026-06-21T18:04:05.123Z"
}
]
}
An $autocapture click with an element fingerprint#
{
"api_key": "revu_pk_example12345678",
"batch": [
{
"event_id": "0a1b2c3d-4e5f-4061-8273-8495a6b7c8d9",
"anonymous_id": "a0e1d2c3-b4a5-4677-8899-aabbccddeeff",
"user_id": "user_8675309",
"session_id": "11112222-3333-4444-5555-666677778888",
"sequence_no": 4,
"platform": "web",
"event_type": "$autocapture",
"screen": "/pricing",
"fingerprint": {
"tag": "button",
"text": "Start free trial",
"role": "button",
"selector": "main > section.cta button.primary",
"ordinal": 0
},
"properties": {
"path": "/pricing"
},
"context": {
"environment": "production",
"sdk_version": "0.1.0"
},
"device_time": "2026-06-21T18:04:11.880Z"
}
]
}
Responses#
| Status | Meaning |
|---|---|
204 | Batch accepted. Empty body. Already-seen event_ids in the batch are de-duplicated. |
401 | The api_key is unknown, or its environment is inactive. |
403 | The api_key is valid but the request Origin is not in the key's allowed-origins allowlist. |
422 | The body failed schema validation (a missing or malformed field, or a batch outside 1 to 200 events). |
429 | The api_key's per-minute rate limit is exhausted. Carries a Retry-After header (seconds). SDK transport treats this as a backoff-and-retry signal; events stay queued. |
500 | Unexpected server error. The SDK backs off and retries; events stay queued client-side. |
Every non-204 response carries this body:
Error#
| Field | Type | Required | Constraints | Description |
|---|---|---|---|---|
error | string | yes | Human-readable error message. | |
code | string | yes | Stable machine-readable code: UNAUTHORIZED, FORBIDDEN, VALIDATION_ERROR, RATE_LIMIT_EXCEEDED, or OPERATION_FAILED. | |
timestamp | string | yes | format: date-time | ISO-8601 time the error was produced. |