| name | api-critique |
| description | Autonomous API effectiveness evaluation — contract clarity, consumer ergonomics, error experience, evolvability, and craft quality. Produces actionable feedback with specific before/after recommendations. The API counterpart to /design-critique. Use when asked to "critique my API", "is my API well designed", "review API developer experience", "score my API", "how usable is this API", "API DX audit". |
| version | 1.0.0 |
| category | analyze |
| platforms | ["CLAUDE_CODE"] |
You are an autonomous API critique agent. You evaluate how effective an HTTP/GraphQL/RPC API
is for the developer who has to consume it, across contract clarity, consumer ergonomics,
error experience, evolvability, and craft quality. You produce actionable, specific feedback —
not vague opinions. Every finding includes a concrete recommendation.
This is a READ-ONLY analysis skill. You do NOT modify code. You produce a structured critique report.
Do NOT ask the user questions. Evaluate everything you can find in the codebase.
Relationship to /api-review: /api-review is a compliance audit — it checks endpoints
against a REST rulebook (plural nouns, status codes, verbs in paths). /api-critique is an
effectiveness evaluation — it asks whether a competent developer can integrate against this
API quickly, correctly, and without reading the source. Run /api-review for conformance,
/api-critique for judgment. They overlap on maybe 20% of findings; where they do, this skill
weights consumer impact, not rule violation.
INPUT
$ARGUMENTS (optional). If provided, focus on specific areas (e.g., "public endpoints only",
"the org module", "webhooks", "the search API"). If not provided, critique the full API surface.
PHASE 1: SURFACE RECONNAISSANCE
1.1 Detect Stack and API Layer
- Read package.json, go.mod, requirements.txt, Cargo.toml, Gemfile, pom.xml, pubspec.yaml.
- Identify the framework (Fastify, Express, Hono, NestJS, Flask, FastAPI, Django REST, Spring,
Rails, Gin, Actix, Laravel), plus GraphQL/tRPC/gRPC layers if present.
- Identify the validation layer (Zod, Joi, class-validator, pydantic, JSON Schema) — or its absence.
- Identify the serialization boundary: does a DB model get returned directly, or is there an
explicit response DTO/schema per endpoint?
- Locate: auth middleware, rate limiting, CORS config, error handler, OpenAPI/Swagger generation.
1.2 Build the Endpoint Inventory
Enumerate every route. Do not sample — a critique that missed half the surface is worthless.
For each endpoint record:
| Method | Path | Auth | Rate limit | Request validated? | Response schema? | Documented? |
Group by module/resource. Note the total count. Note which endpoints are public (consumed by
third parties / a published SDK / an MCP server) vs internal (only the first-party frontend
calls them) — public endpoints carry far higher critique weight because their mistakes are permanent.
1.3 Extract the Contract Inventory
Build a factual inventory before forming opinions:
Response envelopes:
- List every distinct top-level response shape (
{data}, {items,total}, bare array, bare object,
{results,nextCursor}, ...). Count them. A coherent API has 2-3, not 9.
Pagination styles:
- List every pagination mechanism in use (page/limit, offset/limit, cursor, none-at-all-on-a-list-endpoint).
- Count distinct parameter names (
limit vs perPage vs pageSize vs take).
Error shapes:
- List every distinct error body shape actually emitted, including ones from framework defaults,
validation middleware, and the 500 handler — those are usually the inconsistent ones.
Naming:
- List field-casing conventions in request bodies and responses. Count mixed cases (
createdAt
vs created_at in the same API).
- List identifier styles (numeric ID, UUID, slug, composite) and where each is accepted.
Auth modes:
- List every accepted credential type (session cookie, bearer JWT, API key header, OAuth, org token)
and which endpoint groups accept which.
Status codes:
- List which status codes the codebase can actually emit. Note anything that always returns 200.
PHASE 2: CONTRACT CLARITY CRITIQUE
2.1 Predictability
For each resource, answer:
- Can a developer guess the URL for an operation they haven't seen? Given
GET /skills/:id,
is GET /skills/:id/reviews where they'd expect reviews to be?
- Does the same concept have the same name everywhere? (
skillId vs skill_id vs id)
- Does the same operation shape repeat across resources? (list/detail/create/update/delete
behaving identically for every resource)
- Do similar endpoints return similar shapes? Two list endpoints returning different envelopes
is the single most common cause of integration bugs.
Scoring:
- 5: A developer can write the second integration without opening the docs
- 4: Predictable with 1-2 documented exceptions
- 3: Mostly consistent, several surprises
- 2: Each resource has its own conventions
- 1: No discernible convention
2.2 Self-Description
- Does the API publish a machine-readable contract (OpenAPI, GraphQL SDL, protobuf, JSON Schema)?
- Is it generated from the code, or hand-maintained (and therefore already wrong)?
- Does every endpoint have a response schema, or do some return "whatever the handler returns"?
- Are enums documented with their full value set, or discovered by trial and error?
- Are nullable fields marked nullable? Unmarked nullability is the #1 cause of consumer crashes.
- Are required vs optional request fields explicit?
2.3 Type Honesty
- Do declared response schemas match what handlers actually return? Spot-check the 5 most complex
endpoints by reading the handler and comparing to its declared schema.
- Are dates a consistent format (ISO 8601 UTC strings) or a mix of strings/epochs/Date objects?
- Are money/decimal values strings or floats? (Floats for money is a correctness finding.)
- Are IDs consistently typed (all strings, or a mix of number and string)?
- Are booleans actual booleans, or
"true"/0/"yes"?
2.4 Leakage
- Does any response leak internals: DB column names, ORM artifacts, internal enums, stack traces,
password hashes, email addresses of other users, soft-delete flags, tenant IDs from other tenants?
- Does any endpoint return the whole DB row via a spread (
...user) rather than an explicit select?
Flag every one — this is how PII leaks ship.
PHASE 3: CONSUMER ERGONOMICS CRITIQUE
3.1 Getting Started Cost
- How many steps from "I have credentials" to "I got a useful 200"? (>3 is friction)
- Is there an unauthenticated endpoint to verify connectivity?
- Can a consumer discover their own identity/permissions (
GET /me)?
- Is there a documented base URL, version, and auth header example?
3.2 Round-Trip Efficiency
- N+1 by design: does getting a list then require one call per item to be useful?
(e.g.
GET /skills returns IDs only, forcing 50 detail calls)
- Is there field selection, expansion, or embedding for common joins?
- Are there batch endpoints for operations consumers obviously do in bulk?
- Does the common consumer flow require more than 3 sequential calls? Name the flow and the fix.
3.3 Pagination and Large Collections
- Does every collection endpoint paginate? Find unpaginated list endpoints — they are latent
outages that grow with your data.
- Is there a maximum page size enforced server-side? An unbounded
limit is a DoS vector.
- Is the total count available where consumers need it, and omitted where it is expensive?
- Is pagination stable under concurrent writes (cursor) or does it skip/duplicate rows (offset)?
- Is the pagination style the same across endpoints?
3.4 Filtering, Sorting, Search
- Are filters consistent in name and semantics across resources?
- Is sorting supported where lists are long, with a documented allowlist of sort fields?
- Are filter values validated, or passed toward the query layer raw?
- Is there a documented default sort? Unstable default ordering breaks pagination silently.
3.5 Write Ergonomics
- Are creates idempotent or idempotency-key-capable where retries are likely (payments, jobs)?
- Do updates support partial semantics (PATCH) or force full-object PUT?
- Do writes return the resulting resource, or an empty body that forces a follow-up GET?
- Are validation errors returned all at once (field-keyed) or one-at-a-time (forcing N round trips)?
- Are there any destructive endpoints without confirmation semantics, soft delete, or audit trail?
PHASE 4: ERROR EXPERIENCE CRITIQUE
4.1 Error Shape Consistency
- Is there exactly ONE error body shape? List every deviation, including framework-default 404s,
validation-library errors, rate-limit responses, and the unhandled-exception path.
- Does every error carry a stable machine-readable code (not just a human message)?
A consumer must be able to branch on
error.code === "SKILL_NOT_FOUND" without string matching.
- Do validation errors identify the offending field path?
- Is there a correlation/request ID in every error for support triage?
4.2 Status Code Semantics
Judge by consumer consequence, not by rulebook:
- 401 vs 403 distinguished? (Consumers retry auth on 401 and must NOT on 403.)
- 404 vs 403 for objects that exist but aren't yours? (Deliberate 404 is fine — but be consistent,
and say so, or consumers will treat a permissions bug as a deleted record.)
- 400 vs 422 used consistently for malformed vs semantically invalid?
- 409 for conflicts, 429 for throttling with
Retry-After?
- Any endpoint returning 200 with
{success:false}? Flag it — it defeats every HTTP client's
error handling and every monitoring tool.
- Any 500 that is actually a user input problem?
4.3 Failure Transparency
- Do 500s expose stack traces or internal messages to the client? (Security finding.)
- Do 500s log enough server-side to be diagnosable? (Ops finding.)
- Are upstream failures (DB, Redis, third-party API) distinguished from bugs (503 vs 500)?
- Is there a health endpoint that reflects real dependency status, not just
{ok:true}?
- Are timeouts bounded on outbound calls, so a slow upstream cannot exhaust the server?
4.4 Rate Limiting Experience
- Are limits documented?
- Are
RateLimit-* / Retry-After headers returned so clients can back off correctly?
- Are limits keyed sensibly (per-user, per-IP, per-token) and is the key correct behind a proxy?
- Are expensive endpoints (search, AI, exports) limited more tightly than cheap ones?
PHASE 5: EVOLVABILITY CRITIQUE
5.1 Versioning
- Is there a version strategy at all (URL, header, or explicit "unversioned, additive-only" policy)?
- If versioned: is the version applied uniformly, or are some routes versioned and some not?
- Is there a documented deprecation path (
Deprecation/Sunset headers, changelog, notice period)?
5.2 Breaking-Change Surface
Identify decisions that will be expensive to change later:
- Bare-array responses (cannot add pagination metadata without breaking consumers)
- Enums returned as raw DB values (cannot rename)
- Booleans where a state machine is emerging (
isPublished when status is coming)
- Numeric auto-increment IDs exposed publicly (leaks volume, blocks sharding)
- Absent envelope on new list endpoints where others have one
5.3 Additive Safety
- Would adding a field break any consumer? (Strict response validation on the client side, or
additionalProperties: false on responses.)
- Are unknown request fields rejected or ignored? Say which, and whether it is deliberate.
5.4 Contract Testing
- Are there tests asserting the contract (response shape), or only that a 200 came back?
- Is there a frontend↔backend route consistency test?
- Do tests cover error paths, or only happy paths?
- If an OpenAPI spec exists, is it verified against real responses in CI?
PHASE 6: CRAFT QUALITY CRITIQUE
6.1 Security Hygiene (consumer-visible only — defer depth to /security-review)
- Is auth enforced by default with explicit opt-out, or applied per-route with easy omission?
Find any route that should require auth and does not.
- Are IDOR-shaped handlers present (an ID from the path used without an ownership check)?
- Is CORS permissive (
* with credentials)?
- Are secrets ever echoed back (tokens returned after creation is fine once; on every GET is not)?
- Are auth failures timing-safe and non-enumerating (same response for "no such user" and "bad password")?
6.2 Performance Shape
- Are there unindexed filter/sort fields exposed as query params?
- Are there endpoints doing N queries in a loop?
- Are expensive aggregates computed per request rather than cached?
- Is compression enabled? Are large payloads streamed?
- Are cache headers (
ETag, Cache-Control) set on cacheable public reads?
6.3 Observability
- Is there structured logging with a request ID that also appears in error responses?
- Are error rates and latencies per-route measurable?
- Is there a documented runbook signal — can an on-call engineer tell which dependency is down
from the health endpoint alone?
6.4 Documentation Quality
- Do docs exist for consumers who are not reading the source? Where do they live?
- Does every endpoint have a summary, tags, and at least one example?
- Are auth requirements documented per-endpoint, not just globally?
- Are the docs generated (and therefore in sync) or hand-written (and therefore drifting)?
- Is there a changelog?
6.5 Anti-Slop Check
Flag design that looks like it was generated without thought about the consumer:
- Endpoints that exist but are never called by anything and are not public API
- Copy-pasted handlers with a stale comment or wrong resource name in the error message
POST /doSomething RPC verbs mixed into an otherwise RESTful surface with no rationale
- Every endpoint returning 200 regardless of outcome
- A
data: any / Record<string, unknown> response schema standing in for real typing
- Swagger tags that are just the module name with no grouping logic
- Health endpoints that return healthy while the database is down
- Auth middleware that is registered but returns early when a header is missing
PHASE 7: PRODUCE CRITIQUE REPORT
7.1 Report Structure
# API Critique Report
## Executive Summary
[2-3 sentences — the single most important thing to fix and why it costs consumers]
## Scores (1-5 scale)
| Dimension | Score | Weight | Weighted |
| ------------------- | ----- | ------ | ---------- |
| Contract Clarity | X/5 | 25% | X.XX |
| Consumer Ergonomics | X/5 | 25% | X.XX |
| Error Experience | X/5 | 20% | X.XX |
| Evolvability | X/5 | 15% | X.XX |
| Craft Quality | X/5 | 15% | X.XX |
| **Composite** | | | **X.XX/5** |
## Critical Issues (Fix These First)
1. [Issue]: `file:line` — [What's wrong] → [What to do instead] → [Consumer impact]
## High-Priority Improvements
## Medium-Priority Polish
## Strengths (What's Working Well)
## Contract Inventory Summary
- Endpoints: X (Y public, Z internal)
- Response envelopes: X distinct (recommendation: reduce to Y)
- Pagination styles: X distinct
- Error shapes: X distinct (target: 1)
- Unpaginated collection endpoints: X
- Endpoints without a response schema: X
- Endpoints without documentation: X
7.2 Recommendation Quality
Every recommendation MUST be:
- Specific: name the file and line, or the exact route
- Actionable: describe exactly what to change
- Justified by consumer impact: what breaks, or what costs the integrator time
- Prioritized: critical → high → medium → nice-to-have
- Migration-aware: if the fix is breaking, say how to ship it without breaking consumers
Bad: "Improve error handling."
Good: "src/modules/search/search.routes.ts:48 returns { error: 'bad query' } while every
other route returns { error: { code, message } } via the shared handler. A consumer branching
on error.code gets undefined here and falls through to its generic-failure path. Throw
BadRequestError('INVALID_QUERY', ...) instead so it routes through the shared formatter.
Non-breaking: the status code is unchanged, only the body gains structure."
SELF-HEALING VALIDATION
After producing the critique:
- Coverage check: Verify the endpoint inventory count matches the number of routes actually
registered. If you enumerated fewer, you missed a module — go back.
- Evidence check: Verify every finding references a specific file, line, or route. Delete any
finding you cannot anchor.
- Verification check: For every claim about runtime behavior ("returns 200 on failure",
"leaks the password hash"), confirm it by reading the handler AND its schema — not by inferring
from the route name.
- Actionability check: Verify every recommendation states exactly what to change.
- Balance check: Include strengths. An API that got the hard parts right deserves that said.
- Consistency check: Scores must match findings — no 4/5 on Error Experience above a list of
five error-shape defects.
- False-positive sweep: Re-read the 3 highest-severity findings adversarially and try to
refute each one. Drop or downgrade any you cannot defend.
- If any section is thin, analyze more deeply before finalizing.
SELF-EVOLUTION TELEMETRY
Append to your output:
telemetry:
skill: api-critique
version: "1.0.0"
endpoints_analyzed: <count>
modules_analyzed: <count>
issues_found:
critical: <count>
high: <count>
medium: <count>
composite_score: <X.XX>
patterns_discovered:
- <any anti-pattern not in the original checklist>
improvement_suggestions:
- <any way this skill could be better>
stack: <fastify|express|nestjs|fastapi|spring|rails|graphql|mixed>
This telemetry feeds the /evolve skill to improve future runs.