Skip to main content

POST /v1/events

Ingest one event. Runs the rule engine against the user's open missions and broadcasts any progress changes via SSE.

Request

curl -X POST https://api.questkit.jairukchan.com/v1/events \
-H "Authorization: Bearer <JWT>" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: abc123-uuid" \
-d '{
"userId": "usr_demo_123",
"name": "purchase.completed",
"payload": { "product": "boots", "amount": 49.99 },
"timestamp": 1716100000000
}'
FieldTypeRequiredDescription
userIdstringyesMust equal the JWT's sub claim, or you'll get a 403.
namestringyesEvent name. Stable values recommended (e.g. purchase.completed).
payloadobjectyesArbitrary key/value data. Used by MissionCriteria.filter.
timestampnumberyesUnix milliseconds. Trusted as-is.
idempotencyKeystringnoOptional body-level key. Header Idempotency-Key wins when both are present.

Idempotency-Key is the recommended location (RFC 9530 / SDK-friendly).

Response — 200 OK

{
"accepted": true,
"eventId": "evt_01HQK7N5...",
"missionsUpdated": ["daily-streak", "spring-buyer"]
}
FieldTypeDescription
acceptedtrueAlways true on a 200.
eventIdstringServer-generated event identifier.
missionsUpdatedstring[]Mission IDs whose progress changed as a result of this event.

Idempotent replay headers

HeaderValueMeaning
X-Idempotent-ReplayhitServed from KV cache (key matched within 24h).
X-Idempotent-Replaydb-hitKV missed; the partial-unique index caught a duplicate write.

Errors

HTTPerror codeMeaning
400invalid_eventBody shape failed validation.
401unauthorizedMissing or invalid JWT.
403user_mismatchbody.userId doesn't match the JWT's sub.
429rate_limitedHit the 100/min cap. See Retry-After header.

Side effects

  • Inserts a row into events.
  • Evaluates every open mission criterion that names this event; upserts mission_progress.
  • Broadcasts mission.progress / mission.completed SDKUpdates via the SSE Hub for this user.
  • Writes a data point to Analytics Engine.
  • Caches the response under (userId, idempotencyKey) for 24 hours.