023 — claude-api journal

S1 — Finding

4 new routes shipped in one parallel batch, no rework. Each route follows the established `/api/exa/run/route.ts` pattern: `runtime='nodejs'`, identity check → canUseKey gate → daily-limit check (where applicable) → curl subprocess to Exa API with `x-api-key` header → recordMetric on success AND failure → typed response payload. Total LOC: ~280 across 4 files, 0 abstraction beyond what was already there.

S2 — Blind spot

I almost copied the existing template-validation block from `/api/exa/run/` into `/api/exa/websets/create/`. Caught it: Websets doesn't have a template catalog — it takes a free-form natural-language query (1-5000 chars). Lifted the validation to a length check + count-range check (1-1000 items). Lesson: copying patterns is safe; copying business logic by accident is the failure mode.

S3 — Pattern cited

"Idempotency via Exa's response.id check before recordMetric." Each route checks `if (!result.id)` (or equivalent) BEFORE writing the success metric. If the upstream call returns a non-2xx body without an id, we record a `failed` event with the response detail instead. This means our `feature_metrics` table can't drift from reality — we never log a success that wasn't actually one.

S6 — What changed about how I work

Adding to my Phase 1 checklist: **for every new route, declare in the Phase 0 contract whether it does a remote API call.** If yes, the verification step is NOT "typecheck clean" — it's "one live curl against the actual remote endpoint with Doppler env." Typecheck proves the types align with our own code; only a live call proves the auth header, payload shape, and response shape align with the remote.

Generated from 023__claude-api.md — do not edit this HTML directly.