Event Ingestion

POST /api/event

Records a single analytics event. This endpoint is called by the tracking script automatically and can also be called directly for server-side event recording.

Authentication: None required. The Origin header is validated against site_ids if that config option is set.

CORS: Fully permissive (Access-Control-Allow-Origin: *) to allow cross-origin calls from the tracking script.

Request Body

{
  "d": "example.com",
  "n": "pageview",
  "u": "https://example.com/pricing",
  "r": "https://google.com/",
  "w": 1920,
  "p": "{\"plan\": \"pro\"}",
  "ra": 99.00,
  "rc": "USD"
}
FieldTypeRequiredDescription
dstringYesDomain / site identifier. Max 256 chars; alphanumeric plus ., -, _, : only.
nstringYesEvent name (e.g. "pageview", "signup", "purchase").
ustringYesFull URL of the page where the event occurred.
rstringNoReferrer URL.
wnumberNoScreen width in pixels (for device-type detection).
pstringNoCustom properties as a JSON-encoded string. Stored in the props column and queryable via json_extract.
ranumberNoRevenue amount (stored as DECIMAL(12,2)).
rcstringNoISO 4217 currency code (e.g. "USD", "EUR"). Maximum 3 characters.

Response

HTTP/1.1 202 Accepted

The response body is empty. 202 means the event was accepted into the buffer. It will be flushed to Parquet on the next flush cycle or when the buffer threshold is reached.

Validation Errors

ConditionStatus
Missing required field (d, n, or u)422 Unprocessable Entity
Empty d, n, or u400 Bad Request
d contains invalid characters or exceeds 256 chars400 Bad Request
Field exceeds length limit (n > 256, u > 2048, r > 2048, p > 4096)400 Bad Request
Request body exceeds 64 KB413 Payload Too Large
Origin header does not match site_ids403 Forbidden
Rate limit exceeded for this site_id429 Too Many Requests

GET /api/event

Pixel-tracking endpoint for environments where JavaScript is unavailable (email, AMP pages, RSS readers). Returns a 1x1 transparent GIF (43 bytes, Content-Type: image/gif).

Authentication: None required.

Query Parameters

ParameterTypeRequiredDescription
dstringYesDomain / site identifier.
nstringNoEvent name (defaults to "pageview").
ustringYesFull URL of the page.
rstringNoReferrer URL.
wnumberNoScreen width in pixels.

Revenue (ra, rc) and custom properties (p) are not supported on the GET endpoint.

Response

HTTP/1.1 200 OK
Content-Type: image/gif
Content-Length: 43

Usage

<img src="https://analytics.example.com/api/event?d=example.com&u=https://example.com/page" width="1" height="1" alt="">

Bot Filtering

When filter_bots = true (default), the server inspects the User-Agent header and discards the event if it matches known bot patterns. A 202 is still returned — the event is silently dropped rather than returning an error.

Privacy Processing

Before the event is stored:

  1. The client IP address is extracted from the request.
  2. A daily-rotating HMAC-SHA256 visitor_id is computed from IP + User-Agent + today's UTC date + MALLARD_SECRET.
  3. The IP address is discarded. It is never written to disk or the database.

Server-Side Example

curl -X POST https://your-instance.com/api/event \
  -H 'Content-Type: application/json' \
  -d '{
    "d": "example.com",
    "n": "server_signup",
    "u": "https://example.com/signup"
  }'