Batch import activities — server token only
const url = 'http://localhost:3000/v1/batch/activities';const options = { method: 'POST', headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'}, body: '{"activities":[{"feed":"user:alice","activity":{"actor":"example","verb":"example","object":"example","target":"example","foreign_id":"example","time":"2026-04-15T12:00:00Z","custom":{},"refs":["example"]}}]}'};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url http://localhost:3000/v1/batch/activities \ --header 'Authorization: Bearer <token>' \ --header 'Content-Type: application/json' \ --data '{ "activities": [ { "feed": "user:alice", "activity": { "actor": "example", "verb": "example", "object": "example", "target": "example", "foreign_id": "example", "time": "2026-04-15T12:00:00Z", "custom": {}, "refs": [ "example" ] } } ] }'Cold-start import: add up to 100 activities in one call. Each item
carries the destination feed ref inline plus the existing add body;
activity.time (existing optional field) carries historical
timestamps, passed through unclamped as for any server-token add.
Quiet — fan-out runs normally but writes no live-mode head keys, and
no webhooks are fired. Idempotent when items supply foreign_id +
time (existing dedupe identity) — a rerun returns the existing rows
without a second fan-out; without it a rerun duplicates. Partial
failure still returns 200 — inspect per-item results.
Authorizations
Section titled “Authorizations”Request Bodyrequired
Section titled “Request Bodyrequired”object
object
Example
user:aliceobject
Ignored and overwritten for user tokens (spec §5).
Requires time when present — VALIDATION_FAILED otherwise. Dedupe is on the
(foreign_id, time) pair; a foreign_id without a caller-pinned time can never
dedupe (each retry would get a fresh server timestamp, silently duplicating),
so the API rejects the combination rather than letting an idempotency
assumption fail quietly. Deliberate safety deviation from GetStream.
For user tokens, clamped to [now - 10 min, now] (spec §5): retries with the same (foreign_id, time) dedupe; future-pinning is impossible. Server tokens pass it through unclamped. Required when foreign_id is set.
object
Objects this activity points at, as type:id. Max 4, no duplicates.
Responses
Section titled “Responses”Envelope accepted. Per-item results, ordered and same-length as the input; a failed item never rolls back the rest.
object
object
Present when ok for users/activities
§8 error code, present when not ok
Examplegenerated
{ "results": [ { "index": 1, "ok": true, "id": "example", "code": "example" } ]}Envelope invalid (0 items, more than 100 items, or malformed shape).
object
object
Present only for VALIDATION_FAILED.
object
Example
{ "error": { "code": "VALIDATION_FAILED" }}User tokens cannot import — batch routes are server token only.
object
object
Present only for VALIDATION_FAILED.
object
Example
{ "error": { "code": "VALIDATION_FAILED" }}Batch rate limit exceeded. Batch routes charge a separate 600 requests/min window per tenant — never the normal tenant limit.
object
object
Present only for VALIDATION_FAILED.
object
Example
{ "error": { "code": "VALIDATION_FAILED" }}