--- convoy: pricing-online-local brief_number: 8 depends_on: [2] recommended_model: composer-2.5 model_tier: standard files: - lib/pricing/pricing-service.js - pages/api/cards/[id]/pricing.js - pages/api/cards/[id]/price-history.js - pages/api/cards/[id].js - test/api/cards-pricing.test.js - test/api/cards-price-history.test.js cross_brief_commitments: - brief: 9 description: | Response shapes from `/api/cards/[id]/pricing` and `/api/cards/[id]/price-history` are the contract Brief 9's UI consumes. Do not rename fields without updating Brief 9. --- # Brief 8: Card pricing + history APIs ## Goal (1 sentence) Expose `GET /api/cards/[id]/pricing` and `GET /api/cards/[id]/price-history` reading DB with optional stale refresh. ## Files in scope (do not edit anything else) - `lib/pricing/pricing-service.js` - `pages/api/cards/[id]/pricing.js` - `pages/api/cards/[id]/price-history.js` - `pages/api/cards/[id].js` - `test/api/cards-pricing.test.js` - `test/api/cards-price-history.test.js` ## Conventions to follow - API route pattern from `.cursor/rules/api-routes.mdc`: method gate, try/catch, manual validation. - `pricing-service.js`: DB-first; live refresh only when stale >24h with Redis single-flight lock. - Include `outbound` links via `outbound-links.js`. - `price-history.js` API: query `card_price_history` last N days (default 90, max 365). ## Acceptance criteria - [ ] `GET /api/cards/123/pricing` returns prices + `pricesUpdatedAt` + `stale` boolean - [ ] `GET /api/cards/123/price-history?days=30` returns `{ points: [...] }` - [ ] 404 when card missing; 400 on invalid id/days - [ ] Live refresh does not throw on upstream failure - [ ] Tests pass - [ ] No scope expansion ## Rationale (≤3 sentences) Phase 1 backend contract for Purchase and Price History tabs. DB-first path respects free API limits; 24h staleness matches convoy spec.