All 22 checks with why-it-matters prose, severity, and cross-references to related audits.
Inconsistent API naming erodes maintainability (ISO-25010:2011 maintainability.modifiability): a mix of kebab-case, camelCase, and plural/singular resource names forces every developer to read each route individually rather than predicting it. Client codebases accumulate one-off special cases for each naming quirk, and search-and-replace migrations become unreliable. When AI generates new routes or a second engineer adds an endpoint, the inconsistency compounds — each addition makes the surface harder to audit and the refactor more expensive.
Why this severity: High because naming fragmentation raises change cost on every future API modification and integration, violating ISO-25010:2011 maintainability.modifiability at the structural level.
saas-api-design.api-consistency.consistent-namingSee full patternUsing GET to delete resources or POST for all mutations violates HTTP semantics in ways that cause real damage: browsers and CDNs will cache or prefetch GET endpoints that actually mutate state, link prefetchers can trigger deletes without user intent, and CWE-749 (exposed dangerous methods) applies when destructive operations are reachable via safe-method verbs. Semantically incorrect methods also break API consumers who rely on HTTP conventions — retry logic in HTTP clients retries GET and PUT as safe/idempotent but not POST.
Why this severity: High because GET-based mutations can be triggered by prefetchers, cached by CDNs, or exploited via CSRF — any of which produces unintended data destruction or exposure.
saas-api-design.api-consistency.http-methods-correctSee full patternInconsistent response envelopes — some routes returning `{ data: ... }`, others returning raw objects, others using `{ success: true, result: ... }` — force every API consumer to write bespoke error-handling logic per endpoint. This violates ISO-25010:2011 compatibility.interoperability and means a new integration can silently swallow errors that use the wrong shape. In practice, it produces bugs where a frontend assumes `data` is present but the route returned the payload at root level.
Why this severity: Medium because envelope inconsistency causes integration bugs and broken error handling but does not directly enable data exfiltration or service disruption.
saas-api-design.api-consistency.consistent-response-formatSee full patternAdding versioning after external consumers exist is extremely expensive: every client integration must be migrated simultaneously or you must maintain two incompatible surfaces indefinitely. Without a versioning strategy, the first breaking change — adding a required field, renaming a resource, changing a status code — breaks all callers with no migration path. ISO-25010:2011 maintainability.modifiability and compatibility.interoperability are both undermined: the API cannot evolve without breaking the contract.
Why this severity: Medium because the cost is future maintainability debt rather than immediate breakage, but retrofitting versioning once external consumers exist is disruptively expensive.
saas-api-design.api-consistency.api-versioning-strategySee full patternUnpaginated list endpoints are a denial-of-service waiting to happen: as data grows, `findMany({})` with no `take` fetches every record in the table into memory, exhausting database connection pool, server RAM, and response bandwidth simultaneously. This maps directly to CWE-770 (allocation of resources without limits) and OWASP API 2023 API4 (unrestricted resource consumption). A single authenticated request can trigger minutes of database load. Cost-per-request grows linearly with data, making unbounded lists a latent cost bomb in addition to a reliability risk.
Why this severity: High because even a single unbounded list query can exhaust database memory and connection pool, causing a denial-of-service condition that affects all users.
saas-api-design.request-response.pagination-list-endpointsSee full patternWithout server-side filtering and sorting, clients must fetch all records and filter client-side — amplifying data transfer, memory use, and backend query cost at every call. Worse, if a sort field is passed unsanitized into an `ORDER BY` clause, it becomes a CWE-89 SQL injection vector (OWASP A03:2021): an attacker can supply a crafted field name to probe column structure or trigger errors that reveal schema. Even the low-severity label here understates the injection risk on the sort parameter.
Why this severity: Low for the missing filter/sort feature itself, but the sort-field injection path elevates actual exploitability — an unsanitized ORDER BY is a direct injection vector.
saas-api-design.request-response.filtering-sortingSee full patternReturning `passwordHash`, internal tokens, or entire database rows in API responses is a direct CWE-200 (exposure of sensitive information) finding and maps to OWASP A01:2021 (broken access control) when those fields reveal authorization state. A single mistakenly-included `passwordHash` field in a user endpoint hands an attacker every credential they need to mount offline dictionary attacks. Over-fetching also inflates response payloads and tightly couples clients to database schema, making schema changes more expensive (ISO-25010:2011 functional-suitability.functional-correctness).
Why this severity: Low severity in aggregate because exploitation requires accessing the endpoint first, but any single sensitive-field leak (passwordHash, internalToken) is independently critical.
saas-api-design.request-response.no-over-fetchingSee full patternAccepting unbounded request bodies maps to CWE-770 (unrestricted resource allocation) and CWE-20 (improper input validation), and is flagged under OWASP API 2023 as unrestricted resource consumption. A single POST request carrying a multi-gigabyte JSON body can exhaust serverless function memory and cause a cold-start cascade, effectively a one-request denial of service. On platforms like Vercel, the 4.5MB default limit is generous enough to permit several hundred kilobytes of JSON per field — far more than typical API payloads need.
Why this severity: Medium because a missing size limit enables memory exhaustion via oversized request bodies, potentially taking down serverless functions for all concurrent users.
saas-api-design.request-response.request-size-limitsSee full patternWhen bulk UI affordances (select-all-and-delete, bulk publish, batch price update) exist but the API offers only single-record endpoints, clients must fire N sequential requests to complete the operation. This is both a performance problem (N round trips, N transaction commits) and a data integrity risk: a network failure partway through leaves records in an inconsistent state with no rollback. ISO-25010:2011 performance-efficiency.resource-utilization is directly undermined, and the user experience degrades as dataset size grows.
Why this severity: Low because the absence of bulk endpoints degrades performance and UX rather than enabling immediate security exploits, but data integrity risk from partial-batch failures can be severe.
saas-api-design.request-response.bulk-operationsSee full patternAbsent rate limiting on authentication endpoints is one of the most commonly exploited API weaknesses — OWASP API 2023 lists it under both API4 (unrestricted resource consumption) and API7 (authentication failures). Without it, an attacker can mount unlimited credential stuffing or brute-force attacks against your login, signup, and password-reset routes at no cost. CWE-770 and CWE-307 (improper restriction of excessive authentication attempts) both apply. In serverless environments, an in-memory rate limit resets on every cold start and is effectively no limit at all.
Why this severity: Critical because unlimited authentication attempts enable automated credential stuffing and brute-force attacks that compromise user accounts with no server-side barrier.
saas-api-design.api-security.rate-limitingSee full patternA single unvalidated endpoint — one handler that reads `req.body.email` directly into a database query without a validation step — is all an attacker needs to trigger injection (CWE-20, OWASP A03:2021), corrupt data with unexpected types, or crash the application with oversized inputs. OWASP A08:2021 (software and data integrity failures) also applies when schema-less input reaches business logic unguarded. TypeScript types do not enforce runtime shapes: JSON.parse gives you `any`, and type assertions are compile-time fiction.
Why this severity: Critical because even a single unvalidated endpoint is an injection and data-corruption vector — the entire API's trust boundary is defined by its weakest input handler.
saas-api-design.api-security.input-validation-allSee full patternUnprotected API endpoints that serve user-specific data or perform mutations are the canonical realization of OWASP A01:2021 (broken access control) and CWE-306 (missing authentication for critical function). The specific AI-generated antipattern — reading a `userId` from the request body rather than the verified session — maps to CWE-639 (authorization bypass through user-controlled key), enabling any authenticated user to read or mutate any other user's data by supplying a different ID. Frontend auth checks are irrelevant: API routes are directly callable via curl.
Why this severity: Critical because any unprotected endpoint exposing user data or mutations is directly exploitable without any authentication, enabling data theft or state corruption at zero friction.
saas-api-design.api-security.auth-required-non-publicSee full patternA wildcard `Access-Control-Allow-Origin: *` on an authenticated endpoint completely voids same-origin protection: any page on any domain can read the response from a cross-origin fetch, bypassing the browser's security model (CWE-942, OWASP A05:2021). Pairing `Allow-Credentials: true` with `Allow-Origin: *` is a misconfiguration that browsers reject, breaking legitimate cross-origin authenticated requests. Missing CORS configuration on APIs that have external consumers silently fails for those clients, producing hard-to-debug network errors.
Why this severity: High because wildcard CORS on authenticated routes enables cross-origin data theft from any attacker-controlled website that can lure an authenticated user into a browser.
saas-api-design.api-security.cors-configuredSee full patternUnrestricted file uploads are the intersection of CWE-434 (unrestricted upload of dangerous file type) and CWE-770 (resource exhaustion). Accepting unbounded file sizes lets attackers fill your storage quota and exhaust memory parsing the upload body. Validating file type only by extension or the caller-controlled `Content-Type` header is trivially bypassed: renaming `exploit.php` to `exploit.png` and setting `Content-Type: image/png` defeats both checks. OWASP API 2023 flags this under API4 (unrestricted resource consumption) and API8 (security misconfiguration).
Why this severity: High because malicious file uploads can exhaust storage, escape to arbitrary code execution if served directly, and bypass type checks that rely on attacker-controlled metadata.
saas-api-design.api-security.file-upload-restrictionsSee full patternWithout idempotency protection, a network timeout followed by a client retry on a payment endpoint triggers a duplicate charge (CWE-362, race condition via repeated mutation). A double-click on a checkout button produces two identical Stripe `createSubscription` calls. The business consequence is immediate: duplicate charges require manual refunds, generate chargebacks, and erode customer trust. This is not a theoretical risk — network retries are the default behavior of most HTTP clients and mobile SDKs, making duplicate invocation the expected path under degraded network conditions.
Why this severity: High because duplicate payment execution from retries is a common real-world event that causes immediate financial and trust damage requiring manual remediation.
saas-api-design.api-security.idempotency-keysSee full patternA webhook endpoint that processes events without signature verification accepts requests from anyone — any attacker who knows your webhook URL can POST a fabricated `payment_succeeded` or `subscription_canceled` event and trigger the corresponding business logic (CWE-345: insufficient verification of data authenticity, OWASP A08:2021). Webhook URLs are not secret: they appear in Stripe dashboards, server logs, and network traces. The signature secret is the only real credential protecting these endpoints.
Why this severity: Medium because exploitation requires knowing the webhook URL, which is less trivially discoverable than a public endpoint, but the impact of a forged event — fraudulent order fulfillment, account privilege escalation — is severe.
saas-api-design.api-security.webhook-validates-payloadsSee full patternGraphQL without query depth and complexity limits is a denial-of-service vector with a trivially crafted payload: a malicious query that nests deeply-related types exponentially expands server-side resolution work while staying small on the wire (CWE-770, CWE-400). OWASP API 2023 flags this as unrestricted resource consumption (API4). A single deeply-nested query can saturate database connections and CPU for seconds, affecting all concurrent users. Without introspection controls, attackers also gain a complete map of your schema for free.
Why this severity: High because a single malicious query with exponential field nesting can exhaust server resources and produce a denial-of-service condition against all users of the API.
saas-api-design.api-security.graphql-depth-limitsSee full patternError responses that return only an HTTP status code with no body, or a plain string message with no machine-readable code, force API consumers to parse free-text to determine what went wrong — fragile, locale-sensitive, and impossible to handle programmatically. Returning stack traces or SQL fragments in production error bodies is a CWE-209 violation (information exposure through error messages) that leaks table names, column names, and internal architecture to any caller who triggers a 500. Consistent, structured error responses are required for ISO-25010:2011 compatibility.interoperability.
Why this severity: High because inconsistent or detail-leaking error responses both break API consumers and expose internal implementation details that assist attackers in crafting targeted exploits.
saas-api-design.api-docs.error-responses-codes-messagesSee full patternAuthenticated API endpoints without explicit `Cache-Control: no-store` headers can be cached by a shared CDN or proxy, serving one user's private data — billing details, profile information, personal records — to the next user who hits the same cache key. This maps to CWE-524 (use of cache-containing sensitive information) and OWASP A02:2021 (cryptographic failures in data protection). Mutation endpoints cached by a CDN can also return stale success responses to clients while the actual operation does not re-execute.
Why this severity: Low because most CDN configurations default to not caching JSON API responses, but any misconfiguration exposes authenticated user data to other users sharing the same cache.
saas-api-design.api-docs.api-cache-headersSee full patternWithout an OpenAPI spec, every external consumer — a mobile app, a third-party integration, a future backend service — must reverse-engineer your API from network traffic or source code. This directly undermines ISO-25010:2011 compatibility.interoperability and raises integration time proportionally with API surface area. Internal teams suffer the same friction: undocumented APIs attract inconsistent usage patterns and make breaking-change detection impossible without grep-and-hope. A spec is also the prerequisite for automated client SDK generation, contract testing, and mock servers.
Why this severity: Low because missing documentation doesn't break runtime behavior, but it compounds integration cost and makes every API change riskier for consumers who can't validate their assumptions.
saas-api-design.api-docs.openapi-docsSee full patternRoute handlers that call external services (AI APIs, payment processors, email providers, slow database queries) with no timeout configuration can hang for the full platform timeout — 30+ seconds on Vercel — blocking the response and consuming a serverless function invocation slot. In serverless environments, this does not just affect the single user: it contributes to concurrency exhaustion (CWE-400, CWE-770) and drives up cost proportionally with hang duration. ISO-25010:2011 reliability.fault-tolerance requires that slow dependencies do not cascade into application-level failures.
Why this severity: Low because most platforms impose a hard ceiling, but hanging handlers degrade UX, inflate cost, and under high load can exhaust concurrency limits for all users.
saas-api-design.api-docs.api-timeout-handlingSee full patternAPI consumers who call deprecated endpoints with no sunset signaling have no way to know they should migrate until the endpoint disappears and their integration breaks in production. The absence of deprecation headers violates ISO-25010:2011 compatibility.interoperability and maintainability.modifiability: the API surface cannot evolve without causing surprise breakage to consumers who were never warned. `Deprecation` and `Sunset` headers are RFC-standard mechanisms that monitoring tools, API gateways, and SDK generators already understand.
Why this severity: Low because no immediate runtime failure occurs, but surprise removal of deprecated endpoints causes production outages in downstream integrations with no graceful migration path.
saas-api-design.api-docs.api-deprecation-strategySee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open API Design Audit