API Design Review: The Analyst's Checklist Before the Contract Is Frozen
Written by Ahmed at Analyst Engineering, a Senior Technical Business Analyst with 10+ years in banking and payments delivery.
Key takeaways
- An API design review happens before build, while changing the contract is cheap; once consumers integrate, every design flaw becomes a breaking change or a permanent workaround.
- The analyst reviews what architects and developers are least placed to judge: whether resources, fields, enums, and state changes match the business domain and process.
- Money should never be a floating point number in an API contract: use a decimal string with an explicit currency, or an integer in minor units, and apply the choice consistently.
- State changes such as cancel or approve are safer as explicit actions like POST /payments/{id}/cancel than as a client-writable status field, because the server can enforce which transitions are legal.
- A consistent error model, such as RFC 9457 Problem Details extended with stable error codes and field paths, lets consumers handle every endpoint's failures with one piece of code.
An API design review examines a proposed contract before anyone builds it, while changes still cost a conversation rather than a migration. Architects check the platform fit and developers check feasibility; the analyst checks what nobody else is placed to judge: whether the resources, fields, enums, and state changes actually match the business. That is where the most expensive API design flaws live.
To review an API design as an analyst, work through eight lenses: business semantics (names, enums, and glossary), operations and state (every process step and legal transition), data types (money, dates, identifiers, lengths, null versus absent), error model, collections (pagination, filtering, sorting), safety (idempotency and concurrency), asynchronous behavior, and evolution. Record each finding with its location, severity, and a concrete suggestion, and agree the blockers before the contract is frozen.
APIs for Analysts, advanced track. Builds on Part 4, how to document an API. Full learning path: APIs for Analysts.
Everything in this article applies to OpenAPI and GraphQL schemas alike. The full method for writing the contract you review is in API Documentation from Scratch.
Why review an API design before it is built?
Because the cost of changing a contract rises sharply at two moments: when code implements it, and far more when consumers integrate with it. After that second moment, renaming a field or changing a type is a breaking change that needs a new version, a deprecation period, and every consumer’s cooperation. Most “we’ll fix it in v2” decisions are never fixed.
A design-first process makes early review possible: the OpenAPI or GraphQL schema is written and reviewed first, then mocks let consumers and testers start while the build proceeds. The review is the gate between draft and implementation.
Who reviews what in an API design review?
Split the review by strength so it is thorough without being a committee.
| Reviewer | Focus |
|---|---|
| Analyst | Domain naming, process coverage, state transitions, field meaning, lengths aligned with downstream systems, business error cases, consumer journeys |
| Architect | Platform standards, security model, integration pattern, versioning strategy, fit with other APIs |
| Developer | Feasibility, performance implications, data ownership, implementation cost |
| Tester | Testability: stable error codes, deterministic behavior, observable side effects |
| Consumer representative | Whether the journey is buildable without extra calls or guesswork |
Lens 1: Do the names and enums match the business domain?
An API’s vocabulary becomes permanent the day consumers integrate, so it must be the business’s vocabulary, not the database’s.
- Resources are domain nouns, plural, with no verbs:
/payments,/refunds,/mandates. Not/createPaymentor/txnProc. - Field names match the glossary. If operations, finance, and the scheme all say “creditor”, the field is
creditor, notbeneficiaryin one API andpayeein the next. The data dictionary is the reference. - No unexplained abbreviations.
amt,ccy, andbenef_nmsave three characters and cost every future reader. - One casing convention everywhere.
camelCaseorsnake_case, never both in one contract. - Enums are complete and future-aware. List every value the business actually uses, and state in the documentation that consumers must tolerate values added later. A status enum missing a rarely used value, such as a pending investigation state, is a production incident on its first occurrence.
Lens 2: Do the operations cover the process and its state changes?
Walk the business process and the resource lifecycle against the endpoints. Every step needs an operation; every legal transition needs a way to trigger it; every illegal one needs to be impossible.
The key design choice is how state changes happen:
| Approach | Example | Risk |
|---|---|---|
| Client-writable status | PATCH /payments/{id} with {"status": "CANCELLED"} | Clients can attempt any transition; rules scatter into validation code |
| Explicit action | POST /payments/{id}/cancel | The server owns the transition and can refuse it with a clear reason |
Explicit actions map directly onto a state machine: each transition becomes an operation with its own preconditions, errors, and audit trail. Cancelling a settled payment returns 409 with PAYMENT_NOT_CANCELLABLE, instead of silently accepting a status write that downstream systems then contradict.
Also check that server-owned fields are read-only. id, status, createdAt, and fees are set by the server. If the request schema lets a client send them, the design invites mass assignment, one of the risks covered in API security testing.
Lens 3: Are the data types safe and unambiguous?
This lens catches the defects that survive every other review, because the field exists; it is just subtly wrong.
| Data | Safe design | Common flaw |
|---|---|---|
| Money | Decimal string plus currency, {"value": "1250.00", "currency": "EUR"}, or integer minor units plus currency, as Stripe’s amount: 2000 | "amount": 1250.00 as a floating point number, or no currency |
| Timestamps | ISO 8601 with offset, 2026-09-15T10:42:00Z | Local time with no zone; mixing epoch seconds and strings across endpoints |
| Dates | ISO 8601 date only, 2026-09-15, named for its meaning: requestedExecutionDate | A field called date holding a timestamp, with no stated meaning |
| Identifiers | Opaque strings | Integers that leak volumes and break when formats change |
| Lengths | maxLength aligned with downstream: ISO 20022 names are 140 characters, Max35Text for references | No limit in the API and truncation somewhere downstream |
| Booleans | Only for genuinely two-state facts | isApproved that later needs “pending”, becoming a breaking change |
| Null vs absent | Stated per field: absent means “not provided”, null means “explicitly none” | Undefined behavior that each consumer guesses |
| Formats | pattern or format for IBAN, BIC, email, country codes | Free text validated nowhere |
The length row is where analysts contribute most. An API accepting 255-character names in front of a payment flow whose messages carry far less is a truncation defect by design, and the ISO 20022 truncation ledger shows how quietly that data disappears.
Lens 4: Is the error model consistent and useful?
Consumers should be able to handle every endpoint’s failures with one piece of code. That needs one structure, stable codes, and field paths.
A strong default is RFC 9457 Problem Details, the IETF standard for HTTP API errors, served as application/problem+json, extended with your own members:
{
"type": "https://api.example.com/problems/validation",
"title": "The request is invalid",
"status": 400,
"detail": "2 fields failed validation.",
"instance": "/v1/payments",
"traceId": "a1b2c3d4",
"errors": [
{ "code": "FIELD_TOO_LONG", "field": "creditor.name", "message": "Maximum 70 characters." },
{ "code": "IBAN_CHECKSUM_INVALID", "field": "creditor.iban", "message": "Checksum failed." }
]
}
Review questions:
- Is there one error structure across all endpoints?
- Are error codes stable strings consumers can switch on, not just messages that may be reworded?
- Does each field error carry a field path?
- Is the 400 versus 422 convention stated and applied consistently?
- Is every business rule in the requirements represented by a specific code?
- Is it clear which errors are safe to retry, as in the error catalogue from Part 4?
- Do errors avoid leaking internal details such as stack traces, SQL, or class names?
Lens 5: Do lists support pagination, filtering, and sorting?
Any endpoint returning a list will eventually return too much. Check the design before that happens.
| Question | Why it matters |
|---|---|
| Is every list paginated with a maximum page size? | Unbounded lists fail at volume and invite resource exhaustion |
| Cursor or offset? | Cursors stay correct while data changes; offsets skip or repeat items when records are inserted mid-read |
| Is the default order stable and documented? | Paging through an unstably ordered list produces duplicates and gaps |
| Are the filters the business needs present? | Reconciliation needs date ranges and statuses; support needs lookup by your own reference |
| Is total count needed, and affordable? | Counting large tables can be expensive; decide deliberately |
Reconciliation is the use case to test this lens against. If operations cannot fetch “all payments settled yesterday” efficiently, the design is incomplete, whatever the happy path looks like.
Lens 6: Are creates and updates safe to retry and to run concurrently?
- Idempotency on creates. Does every
POSTthat creates something or moves money accept an idempotency key, with a documented retention window and a409for key reuse with a different body? See idempotency testing. - Concurrency on updates. When two clients update the same resource, does the design prevent lost updates, for example with an
ETagreturned on read andIf-Matchrequired on update, answering412 Precondition Failedwhen the resource changed in between? - Timeout semantics. Does the documentation tell consumers what to do after a timeout, given the operation may have succeeded?
Lens 7: Are long-running operations designed as asynchronous?
If an operation cannot finish within a normal request timeout, such as settlement, screening, or a large export, the design must not pretend it can.
- The create returns
202 Acceptedor a clearly non-final status such asACCP, with a way to get the outcome. - The outcome is available by polling a status resource, by webhook, or both, with the choice documented.
- Final statuses are named, and the consumer knows which ones end the flow.
- Webhook events are named consistently, and their delivery guarantees are documented, as covered in webhooks explained for analysts.
The difference between these designs is the subject of synchronous vs asynchronous.
Lens 8: Can the contract evolve without breaking consumers?
- Is the versioning strategy decided and stated: path, header, or date-based versions?
- Does the documentation say consumers must ignore unknown fields and tolerate new enum values?
- Are there fields likely to change shape soon, such as a boolean that will need a third state, or a single address string that must become structured? Fix those now.
- Is there a deprecation policy with notice periods?
Designing for evolution is cheaper than managing breakage, and the change side is covered in API versioning and breaking changes.
What does a real design review look like?
Here is a first draft that reaches review more often than anyone admits:
paths:
/createPayment:
post:
requestBody:
content:
application/json:
schema:
type: object
properties:
amt: { type: number }
ccy: { type: string }
date: { type: string }
beneficiary_name: { type: string }
accountNumber: { type: string }
status: { type: string }
responses:
'200':
description: OK
'500':
description: Error
And the review:
| # | Location | Finding | Severity | Suggestion |
|---|---|---|---|---|
| 1 | Path | Verb in path | Major | POST /v1/payments |
| 2 | amt | Money as floating point, abbreviated | Blocker | instructedAmount: { value: "1250.00", currency: "EUR" } as decimal string |
| 3 | ccy | No format or allowed values | Major | ISO 4217 code, enum of supported currencies |
| 4 | date | Ambiguous meaning and format | Blocker | requestedExecutionDate, format: date, rules for past dates and holidays |
| 5 | Casing | snake_case and camelCase mixed | Minor | One convention across the API |
| 6 | beneficiary_name | Term not in glossary; no length | Major | creditor.name, maxLength aligned with downstream messages |
| 7 | accountNumber | Format undefined | Major | creditor.iban with checksum validation, or a typed account object |
| 8 | status | Server-owned field writable by client | Blocker | Remove from request; return in response |
| 9 | Schema | No required list | Major | Mark mandatory fields |
| 10 | Headers | No idempotency key on a money-moving create | Blocker | Required Idempotency-Key header |
| 11 | 200 | Create returns OK with no body | Major | 201 with the created payment and its id, or 202 if asynchronous |
| 12 | Errors | Only 500; no validation or business errors | Blocker | 400, 401, 403, 409, 422 with a shared error schema |
| 13 | Security | No authentication defined | Blocker | Security scheme with scopes |
| 14 | Examples | None | Minor | A realistic named example per request and response |
Fourteen findings in fifteen lines, six of them blockers. Each is a two-minute fix today and a versioned migration in a year. The corrected version of this endpoint is the one in Part 4.
How do you make API design review repeatable?
- Adopt a published style guide instead of inventing one. The Zalando RESTful API Guidelines, the Microsoft REST API Guidelines, and Google’s API Improvement Proposals are all public and detailed.
- Automate the mechanical checks. Linting with Spectral or Redocly enforces casing, required descriptions, error schemas, and examples on every pull request, so humans review meaning rather than formatting.
- Keep a findings log per API. Recurring findings reveal where the style guide or templates need to change.
- Review consumer journeys, not just endpoints. Walk one real flow end to end against the draft; missing operations show up immediately.
The APIs for Analysts learning path
Beginner: What is an API · API glossary · JSON for analysts · HTTP status codes · Your first collection · Why did my API request fail? · Reading an API contract
Intermediate: Analyze an API · Document an API · API test cases · Chaining and scripts · Webhooks · GraphQL
Advanced: POCs and demos · API design review (you are here) · Versioning and breaking changes · API security testing · API tests in CI
The takeaway
Review an API design before build, while changing it is cheap. As the analyst, own the lenses nobody else can judge well: domain names and enums, operations for every process step and state transition, and data types that will not corrupt money, dates, or lengths downstream. Then check the error model, list design, idempotency and concurrency, asynchronous behavior, and evolution plan. Record findings with severity and a concrete fix, automate the mechanical checks with a linter and a public style guide, and spend human review time on meaning.
For writing contracts that pass this review the first time, see API Documentation from Scratch, and for the requirements behind them, From Vague BR to Functional Requirements. Have a draft contract that needs a practitioner’s review? Book a 1:1 Tech BA Coaching Call.
Ahmed is a Senior Technical Business Analyst with 10+ years in banking and payments. He builds practical guides and tools for analysts at The Tech BA Toolkit.
Tags: API Design, OpenAPI, Design Review, Systems Analysis, Payments
About the author
Analyst Engineering is written by Ahmed, a Senior Technical Business Analyst with 10+ years of banking and payments delivery experience: ISO 20022 and SWIFT messaging, payments API integration, Kafka event validation, and production support. Every article comes from real delivery work, and each one is reviewed and updated as tools and standards change.
Related articles
- How to Document an API: What Analysts Write So Developers Integrate Without a Call How to document an API as an analyst: the seven sections consumers need, an OpenAPI endpoint example, an error catalogue, flow guides, and docs you can test.
- How to Write API Requirements That Developers Can Actually Build Write API requirements the right way: endpoint, method, request and response schema, status codes, error contracts, and testable acceptance criteria. With examples.
- API Versioning and Breaking Changes: How Analysts Assess Impact Before a Release What counts as a breaking API change, versioning strategies, Deprecation and Sunset headers, detecting breaks with oasdiff, and consumer impact assessment.
- State Machines for Payments: Every Status, Every Transition How to model a payment as a state machine: define the states, the allowed transitions, the triggers, and the illegal moves. The tool that makes status behavior precise.
Free account
Practice on the Labs, keep your progress
A free account, no password: an email link signs you in. It saves your steps and self-assessments on the Labs, shows your missions on a dashboard, unlocks the solutions, and, if you tick the box, sends you new missions and articles when they ship.
Your email is used to sign you in. Nothing else, unless you ask. Privacy.