Skip to main content

API Overview

The QuestKit API is a JSON-over-HTTPS service hosted on Cloudflare Workers. One Worker (questkit-worker-api) owns every public endpoint listed in this section.

Base URL

https://api.questkit.jairukchan.com

All endpoints are versioned under /v1.

Authentication

Every endpoint except POST /v1/auth/token requires:

Authorization: Bearer <JWT>

The JWT is issued by POST /v1/auth/token from (appId, appSecret, userId). It's signed HS256 with the Worker's JWT_SECRET, carries a 1-hour expiry, and includes a unique jti claim. JWTs can be revoked instantly via a KV denylist keyed by jti.

Mint tokens on your backend. Never ship APP_SECRET to the browser.

Content type

Requests and responses are application/json unless noted otherwise. The SSE endpoint (GET /v1/sse/updates) returns text/event-stream.

Rate limits

A Durable Object enforces per-JWT sliding-window limits on ingestion-style endpoints:

EndpointLimitWindow
POST /v1/events100 requests60 s
other readsno DO limit

When you cross the threshold, you get:

HTTP/1.1 429 Too Many Requests
Retry-After: 12
Content-Type: application/json

{ "error": "rate_limited", "retryAfterMs": 12000 }

Idempotency

For mutating endpoints (/v1/events, /v1/missions/:id/claim), pass an Idempotency-Key header. The server caches the response for 24 hours per (userId, key) tuple and returns the cached body on replay:

HTTP/1.1 200 OK
X-Idempotent-Replay: hit

A db-hit value means the partial-unique index caught a replay the KV layer missed (rare race window). Both are safe.

Error model

Errors come back as { "error": "<code>", "message"?: "..." } with a meaningful HTTP status.

HTTPerror codeMeaning
400validation_errorBody shape failed validation.
400invalid_eventPOST /v1/events body failed validation.
401invalid_credentialsPOST /v1/auth/token rejected the secret.
401unauthorizedMissing or invalid JWT.
403user_mismatchEvent body's userId doesn't match the JWT's sub.
404mission_not_foundThe mission doesn't exist.
404balance_not_foundNo balance row for (userId, currency).
404campaign_not_foundThe campaign doesn't exist.
409claim_not_readyMission isn't completed yet (or no progress row).
429rate_limitedSliding-window cap hit. See Retry-After.

Note: AI-recommendation failures used to surface as 502 ai_response_malformed / 503 ai_unavailable. Since v0.1.4 those failure modes are absorbed by the route: GET /v1/recommendations returns 200 { fallback: true, ... } instead. See /v1/recommendations.

CORS

The API accepts cross-origin requests with Authorization in the preflight. There's no Origin allowlist — the SDK is designed to run on any host. Tokens are scoped per-app + per-user, which is the boundary that matters.

Where to next