Skip to Content
DocsAPIEvents

Events API

Ingest events from SDKs and server-side integrations via EventsService.BatchCreate.

Service: EventsService
Auth: SDK API key
Protocol: Connect RPC

BatchCreate

Send one or more events in a single RPC call. This is the only ingestion method — there is no single-event endpoint.

Request

{ "events": [ { "name": "page_view", "properties": { "path": "/pricing", "title": "Pricing" }, "timestamp": "2026-06-01T14:30:00Z", "sessionId": "sess_abc123" }, { "name": "purchase", "properties": { "revenue": 29.99, "currency": "USD", "order_id": "ord-456" }, "timestamp": "2026-06-01T14:32:00Z", "sessionId": "sess_abc123" } ] }

Request fields

FieldTypeRequiredDescription
eventsarrayYesOne or more events (max batch size enforced server-side)
events[].namestringYesEvent name
events[].propertiesmapNoKey-value properties (string, number, boolean)
events[].timestamptimestampNoEvent time; defaults to server receive time
events[].sessionIdstringNoSession identifier; SDK-managed in client integrations

Response

{ "eventsCreated": 2 }

TypeScript client

import { createClient } from '@connectrpc/connect' import { createConnectTransport } from '@connectrpc/connect-web' import { EventsService } from './gen/events/v1/events_connect' const transport = createConnectTransport({ baseUrl: 'https://api.pug.sh', interceptors: [ next => async req => { req.header.set('Authorization', `Bearer ${process.env.PUG_SDK_API_KEY}`) req.header.set('x-project-id', process.env.PUG_PROJECT_ID!) return next(req) } ] }) const client = createClient(EventsService, transport) await client.batchCreate({ events: [{ name: 'purchase', properties: { revenue: 29.99, currency: 'USD', order_id: 'ord-456' }, timestamp: new Date() }] })

curl (Connect protocol)

curl -X POST https://api.pug.sh/events.v1.EventsService/BatchCreate \ -H "Authorization: Bearer YOUR_SDK_API_KEY" \ -H "x-project-id: YOUR_PROJECT_ID" \ -H "Content-Type: application/json" \ -H "Connect-Protocol-Version: 1" \ -d '{ "events": [{ "name": "page_view", "properties": {"path": "/home"} }] }'

Pipeline

Events are processed asynchronously after ingestion:

BatchCreate → validate (protovalidate + well-known event schemas) → publish to NATS → event workers enrich: $geo.country, $geo.city, $geo.region (from IP) $browser, $browserVersion (from User-Agent) $os, $device $botScore (bot detection) → write to ClickHouse → available in Insights queries

Typical latency: 2–10 seconds from ingestion to appearing in Live.

Well-known events

Standard event names have protobuf-defined property schemas validated at ingestion:

{ "name": "purchase", "properties": { "revenue": 29.99, "currency": "USD", "order_id": "ord-456" } }

Invalid properties on well-known events return invalid_argument. See Well-known events.

Custom event names skip schema validation — any properties accepted.

Batching guidance

ContextRecommendation
Browser SDKUse client-side batching (default) — SDK handles this
Server-side high volumeBatch up to server limit per RPC call
Critical conversionsSend immediately in a single-event batch
Offline replayBatch by time window; set accurate timestamp

The Web SDK batches client-side with localStorage persistence. Server integrations should batch for efficiency but send critical events immediately.

Querying events

Events are not queried directly via EventsService. Use:

Errors

ErrorCauseFix
unauthenticatedInvalid API keyCheck Settings → API keys
invalid_argument on nameEmpty event nameProvide non-empty string
invalid_argument on propertiesSchema validation failedCheck Well-known events
invalid_argument on batch sizeToo many events in one callReduce batch size

Further reading

Last updated on