Skip to content
API

This site has a real contract

Every public endpoint behind /status, the guestbook, and the live BFF sandbox — documented as an actual OpenAPI spec, not a description of one. Retroactive documentation of code that already existed, worth saying plainly rather than implying otherwise.

The raw contract

Real OpenAPI 3.0.3 JSON, servable and importable — paste it into Postman, Insomnia, or a real Swagger UI elsewhere. Response examples below show realistic shapes; exact numbers will differ every time you call these endpoints live, same as the sandbox they describe.

/api/openapi.json
Endpoints

Vitals

Real-user Core Web Vitals behind /status.

GET/api/vitals

p75 summary across recent samples

No rate limit, no auth — read-only aggregate, safe to call freely.

Responses
200Per-metric p75 summary.
200.json
1{2  "summary": [3    {4      "metric": "LCP",5      "p75": 2380,6      "samples": 214,7      "rating": "good"8    },9    {10      "metric": "INP",11      "p75": 180,12      "samples": 190,13      "rating": "good"14    }15  ],16  "totalSamples": 214,17  "since": 172590000000018}
Try it
POST/api/vitals

Report one Core Web Vitals sample

Called by every visitor's browser automatically (src/components/vitals-reporter.tsx). Rate-limited per IP so a script cannot dominate the sample /status computes p75 from.

Request body
request.json
1{2  "metric": "LCP",3  "value": 2143.5,4  "path": "/work/bff-layer"5}
Responses
204Recorded — no body.
400Malformed JSON, an unknown metric name, or a value outside the sane range.
400.json
1{2  "error": "unknown metric"3}
429Rate limited — 60 requests/minute per IP.
429.json
1{2  "error": "too many requests"3}
Try it

Not triggerable from this page — this one has real side effects.

curl
1curl -X POST \2  -H "Content-Type: application/json" \3  -d '{"metric":"LCP","value":2143.5,"path":"/work/bff-layer"}' \4  https://chandansm.in/api/vitals
Endpoints

Guestbook

The public guestbook at /guestbook.

GET/api/guestbook

List guestbook entries, newest first

Responses
200All entries currently stored.
200.json
1{2  "entries": [3    {4      "id": "afd3fb5c-b69f-457d-979a-e558d4200976",5      "name": "A visitor",6      "message": "Hello!",7      "at": 17893086720468    }9  ]10}
Try it
POST/api/guestbook

Post a new entry

Rate-limited to 5 requests/minute per IP — a guestbook is a slow trickle from real people, not a stream.

Request body
request.json
1{2  "name": "Ada",3  "message": "Loved the BFF sandbox."4}
Responses
201Entry created.
400Empty or missing message.
400.json
1{2  "error": "message is required"3}
429Rate limited.
429.json
1{2  "error": "too many requests"3}
Try it

Not triggerable from this page — this one has real side effects.

curl
1curl -X POST \2  -H "Content-Type: application/json" \3  -d '{"name":"Ada","message":"Loved the BFF sandbox."}' \4  https://chandansm.in/api/guestbook
DELETE/api/guestbook

Delete an entry (admin only)

No user accounts exist on this site — this is gated by a shared secret (GUESTBOOK_ADMIN_TOKEN), not a login. Fails closed: if the server has no token configured, every request is rejected, not silently allowed.

Request body
request.json
1{2  "id": "afd3fb5c-b69f-457d-979a-e558d4200976"3}
Responses
204Deleted — no body.
401Missing, wrong, or unconfigured token.
401.json
1{2  "error": "unauthorized"3}
404No entry with that id.
404.json
1{2  "error": "not found"3}
429Rate limited — 20 requests/hour per IP, separate from the posting limit, to slow down token-guessing specifically.
Try it

Not triggerable from this page — this one has real side effects.

curl
1curl -X DELETE \2  -H "Authorization: Bearer <GUESTBOOK_ADMIN_TOKEN>" \3  -H "Content-Type: application/json" \4  -d '{"id":"afd3fb5c-b69f-457d-979a-e558d4200976"}' \5  https://chandansm.in/api/guestbook
Endpoints

Sandbox

The live BFF sandbox at /work/bff-layer.

GET/api/sandbox/{service}

Call one simulated microservice directly

"Client orchestrates" mode for the live sandbox on /work/bff-layer: real artificial latency, called one at a time by the client. Shares a 60 requests/minute per-IP budget with /api/sandbox/screen.

Parameters
service(path, required)
— one of customer, eligibility, offers, merchant, policy, limits
fail(query)
Set equal to the service name to simulate that dependency failing.
Responses
200The service responded.
200.json
1{2  "service": "customer",3  "ok": true,4  "ms": 116,5  "data": {6    "customerId": "cus_demo_01",7    "displayName": "Jordan Ellis"8  },9  "simulated": true10}
404Not one of the six known service names.
404.json
1{2  "error": "unknown service"3}
429Rate limited.
503The simulated dependency failed (only when ?fail= matches this service).
503.json
1{2  "service": "offers",3  "ok": false,4  "ms": 121,5  "error": "offers unavailable (simulated)",6  "simulated": true7}
Try it
GET/api/sandbox/screen

Call all six simulated services through one aggregator

"BFF composes" mode: a real Promise.all fan-out server-side, each leg individually timed and wrapped so one failure never rejects the whole response — returned to the client as a single round trip. Shares its rate-limit budget with /api/sandbox/{service}.

Parameters
fail(query)
Comma-separated service names to simulate as failing, e.g. offers,policy.
Responses
200Always 200 — individual field failures surface inside the body, never as a top-level error.
200.json
1{2  "ok": true,3  "simulated": true,4  "totalMs": 189.4,5  "serverTimings": {6    "customer": 119,7    "eligibility": 160,8    "offers": 121,9    "merchant": 111,10    "policy": 158,11    "limits": 6312  }13}
429Rate limited.
Try it
Always up for a good problem

Let's build something worth maintaining

Have a platform problem, a team that needs a hand, or just want to argue about API design? My inbox is open.