POST /v1/events/batch

Send a batch of events

Use this for SDK transports or anything sending more than ~1 event per second. The SDKs batch automatically; the endpoint is documented here so customers writing their own integrations can hit it directly.

POST /v1/events/batch HTTP/1.1
Host: api.getfluxly.com
Authorization: Bearer gflux_pub_abcdef1234
Content-Type: application/json
X-Idempotency-Key: 7d6f1a40-9c2b-4d1e-9b32-7a4e0f55c2e1

{
  "events": [
    {
      "event": "page_viewed",
      "anonymous_id": "anon_a8f3c2",
      "properties": { "path": "/pricing" },
      "timestamp": "2026-05-16T07:00:00Z"
    },
    {
      "event": "cta_clicked",
      "anonymous_id": "anon_a8f3c2",
      "properties": { "id": "hero_get_started" },
      "timestamp": "2026-05-16T07:00:02Z"
    }
  ]
}

Batch limits

| Limit | Value | | --- | --- | | Events per batch | ≤ 100 | | Body size | ≤ 256 KB | | Idempotency-key TTL | 24 hours |

Per-event validation rules are the same as POST /v1/events.

Idempotency

If the same X-Idempotency-Key is replayed within the TTL window, the response is the original response. Re-tries from SDKs after a transient network failure should reuse the same key so the backend can dedupe.

Response, 202 Accepted

{
  "accepted": 2,
  "rejected": 0,
  "errors": [],
  "request_id": "req_8f2a1c0d4"
}

Per-event rejections appear in errors[] with an index pointing back at the original events[] array, so a partial-success batch tells the caller exactly which entries to fix:

{
  "accepted": 1,
  "rejected": 1,
  "errors": [
    {
      "index": 1,
      "error": "validation_error",
      "detail": "event name must be snake_case"
    }
  ]
}

Errors

Same table as POST /v1/events. The batch-level errors (payload_too_large, rate_limited) reject the whole request; per-event errors come back inside errors[].