You don't need to write OpenAPI YAML, choose HTTP status codes, or pick between REST and GraphQL. You need to specify the contract: what triggers the call, what data goes in, what comes back, and what "done" and "wrong" look like. Engineers own the implementation; you own the intent, the edge cases, and the acceptance criteria.

Quick answer: A PM's API spec defines the contract — trigger, inputs, outputs, error states, and acceptance criteria — not the implementation. Protocol choice, schema syntax, and endpoint naming belong to engineering. Your job is the "what" and the "what if," written specifically enough that engineering can't accidentally build the wrong thing.

What "API Spec" Actually Means for a PM

"API spec" is really two documents wearing one name. There's a technical specification — the OpenAPI/Swagger schema, request/response types, header formats — that engineering owns and generates from code. And there's the requirements layer: why this endpoint exists, what triggers it, and what must be true when it succeeds or fails. That second layer is yours.

Confusing the two is where most PM-authored specs go wrong. A PM who tries to write the technical spec ends up guessing at implementation details they don't actually control — and an engineer who inherits a vague requirements doc ends up guessing at product intent they shouldn't have to invent. Neither guess is good.

The good news: you don't need new tooling or a computer science degree to close that gap. You need the same discipline behind writing a PRD engineers can actually build from — because with an API, the audience is entirely technical, and ambiguity that a designer might charitably interpret becomes a 400 error or a silent data-loss bug instead.

This is a genuinely technical PM skill, but it's a requirements skill, not a syntax skill. The test isn't whether you can write valid JSON Schema. It's whether an engineer reading your requirement can list every input, every output, and every way the call can fail — without pinging you to ask.

It matters more now than it used to. Most products aren't a single monolith anymore — a mobile app, a partner integration, and an internal automation might all hit the same endpoint with different assumptions about what it guarantees. Roy Fielding's original 2000 dissertation defining REST described the API as an architectural contract between independent systems; that framing holds just as well for a contract between your product team and the one integration partner reading your docs at 11pm.

The Contract: What You Must Specify

A complete API contract has five parts a PM should own: the trigger (what fires the call), the request shape (required vs. optional fields), the response shape (what a caller gets on success), error states (what can go wrong), and acceptance criteria (how everyone knows it's done).

Here's what that looks like as an actual artifact, not a description of one — a fragment for a "start subscription" endpoint:

Feature: Start a paid subscription
Trigger: User completes checkout on the Upgrade page
Actor: Authenticated user with a valid payment method on file

Request contract:
  plan_id            (string, required)  — must match an active plan in the catalog
  payment_method_id  (string, required)  — must belong to the requesting user
  promo_code         (string, optional)  — validated against active promotions only

Response contract (success):
  subscription_id, status ("active" | "trialing"), current_period_end, plan_id

Response contract (failure):
  402 — payment method declined
  404 — plan_id does not exist or is retired
  409 — user already has an active subscription
  422 — promo_code expired or not applicable to this plan

Notice what's absent: no database columns, no URL path, no verb choice, no pagination scheme. Those are implementation. What's present is exactly what a business stakeholder, a QA engineer, and a backend engineer all need from the same page — the fields, their constraints, and every state the call can end in.

An API contract is a promise to every future caller — including the ones you haven't built a UI for yet.

Get the field list and the required/optional flags wrong, and every downstream feature inherits the mistake. This is where API speccing overlaps with data modeling more than most PMs realize: your request and response fields are a schema, whether or not you'd ever call them that.

Turning Requirements Into Acceptance Criteria

Acceptance criteria for an API endpoint should be testable, binary statements — Given/When/Then or a plain checklist — covering the happy path, every documented error state, and at least one boundary condition. If a QA engineer can't turn your criterion into a test case in under a minute, it isn't specific enough yet.

Continuing the subscription example, the acceptance criteria table looks like this:

ScenarioGivenWhenThen
Happy pathValid plan_id and payment method on filePOST /subscriptions is called201, status: "active", subscription_id returned
Declined cardPayment method fails processor checkPOST /subscriptions is called402, no subscription record created
Duplicate subscriptionUser already has an active subscriptionPOST /subscriptions is called409, existing subscription_id returned in the error body
Expired promopromo_code is past its end datePOST /subscriptions is called422, error explains the code expired; subscription is not created at full price by default
Retired planplan_id no longer exists in the active catalogPOST /subscriptions is called404, no partial record created

Two rows on that table matter more than the rest. The "duplicate subscription" and "expired promo" rows exist because someone asked "what should not silently happen" — and that question is a PM's, not an engineer's, to answer. Engineering can tell you a duplicate call is technically possible; only the business can say whether a duplicate call should double-charge the user or return the existing record.

Also worth specifying explicitly: idempotency. What happens if the client retries the same request after a network timeout — does the user get charged twice? Stripe's API popularized idempotency keys precisely because "the network dropped the response, not the request" is a routine failure mode, and it's a business decision, not just an engineering one, whether a retried request should be safe to repeat.

Don't skip the non-functional requirements

Acceptance criteria aren't only about correctness — a few non-functional constraints belong in your spec too, stated as business requirements rather than engineering mechanics:

  • Latency expectations. "Must respond in under 2 seconds at the 95th percentile" is a product requirement if it's blocking a checkout flow; the caching or indexing strategy that hits that number isn't yours to pick.
  • Rate limits. State the volume you actually expect ("up to 50 requests/minute per integration partner"), not a specific throttling algorithm.
  • Data sensitivity. Flag anything touching payment details, health data, or PII explicitly — it changes what engineering needs to build around the endpoint before it changes what the endpoint itself does.

What to Leave to Engineers

Anything below the contract line — database schema, endpoint URL structure, HTTP verb choice, pagination mechanics, caching strategy, internal service boundaries — belongs to engineering. Specifying it anyway doesn't add safety. It removes design freedom, invites rework when real constraints surface later, and signals you don't trust the team to solve a problem you've already framed well.

Marty Cagan's framing in INSPIRED, drawn from decades of work with Silicon Valley Product Group, is the cleanest version of this boundary: product defines the problem and the "what," engineering owns the "how." An API spec is that principle applied at the smallest possible unit — one endpoint instead of one feature — which is exactly why it's tempting to blur the line. The stakes feel small enough to over-manage.

Signs you've crossed into implementation territory:

  1. You've named a field after an internal database column instead of describing the business concept.
  2. You've mandated a specific HTTP verb or status code with no product reason behind the choice.
  3. You're specifying pagination cursor mechanics instead of stating "the list must support 10,000+ rows without timing out."
  4. You've written implementation pseudocode instead of describing observable behavior.

None of this is about staying ignorant of how APIs work — read enough OpenAPI documentation, maintained under the Linux Foundation's OpenAPI Initiative, to sanity-check what engineering ships against what you specified. That's different from writing the schema yourself.

There's also a velocity cost to over-specifying that's easy to miss. Martin Fowler's writing on consumer-driven contracts makes a useful distinction: a contract should encode what a consumer of the API actually needs, verified through tests, rather than everything a producer could expose. Applied to a PM spec, the lesson is the same — describe what the business needs to be true, let the test suite (not your doc) enforce it, and engineering keeps room to refactor the internals without a spec review every time.

A Reusable Template: What's Yours, What's Theirs

The fastest way to keep a spec review from turning into a scope argument is to make the ownership boundary explicit, element by element, before anyone starts writing. Use this table as a starting checklist for any endpoint you're speccing:

Spec ElementPM OwnsEngineer Owns
Trigger & user storyWhy this call exists
Required vs. optional fieldsThe business rule behind eachData types, validation regex
Success response meaningWhat "done" means to the userExact JSON shape, field naming convention
Error conditionsWhich errors are possible, what the user seesHTTP status code mapping, retry semantics
Acceptance criteriaYesTest implementation
Endpoint URL & HTTP verbRarely — only if user-facing (webhooks, public API)Yes
Auth requirementConstraint only ("must require login")OAuth/JWT/session mechanics
Rate limiting & paginationBusiness need only ("must handle 10k+ rows")Mechanics
Database schemaYes

If you're speccing an endpoint that exists to serve a flow you've already mapped in a clickable prototype, start there instead of a blank doc. Pull your field list straight from the form fields, validation states, and empty/error states already built into the wireframe — it's usually a more complete requirements source than working from memory.

Keeping the Spec Alive as the API Evolves

An API spec written once and frozen in a doc rots the moment the first edge case surfaces in code review — a new error state, a renamed field, a scope-narrowed v2. Treat the spec as a living artifact with a visible change history, not a one-time deliverable, and revisit it at every schema-affecting pull request.

This is the same problem a living PRD solves for requirements generally: the written spec drifts out of sync with what actually shipped unless something forces reconciliation. Treating an API contract as a living document rather than a frozen one is the difference between a spec that's still trustworthy in month six and one nobody bothers opening anymore.

It also matters for versioning. Postman's annual State of the API Report has, year after year, found unclear or out-of-date documentation ranking among API consumers' top reported frustrations — and an internal spec that engineering, QA, and support all rely on is really just documentation with a smaller audience. The same decay applies: a contract that's accurate on launch day and silently wrong by v2 is worse than no contract, because people keep trusting it.

Prodinja's Spec Studio is built around that same idea for the requirements layer generally: requirements, acceptance criteria, and open questions live in one structured PRD you can edit and version as the contract evolves, rather than a static doc that's accurate on day one and fiction by sprint three. It's a small mechanical fix for a problem every API spec eventually has — someone changes the contract in a pull request, and the doc doesn't hear about it.

Concretely, that means deciding upfront who owns three recurring questions before the first version ships: what happens to v1 callers when v2 launches, how deprecations get communicated, and where the "current, correct" version of the acceptance criteria lives so engineering isn't cross-referencing three Slack threads and a doc from last quarter.

Key Takeaways

  • You own the contract, not the implementation. Trigger, request/response shape, error states, and acceptance criteria are PM territory; schema syntax, verb choice, and internal architecture are not.
  • Show the actual artifact. A spec fragment with concrete field names, types, and required/optional flags gets built correctly; a paragraph describing "the API should accept subscription details" gets interpreted five different ways.
  • Acceptance criteria are tests, not adjectives. If a QA engineer can't turn your criterion into a pass/fail check in under a minute, rewrite it as Given/When/Then.
  • Error states are half the spec. Most under-specified APIs fail not on the happy path but on the declined card, the duplicate request, or the expired promo code nobody wrote down.
  • Over-specifying is its own failure mode. Naming database columns or mandating HTTP verbs doesn't make a spec safer — it removes the engineering team's freedom to solve a problem you've already framed well.
  • The spec is a living document. Version it, log what changed, and revisit it whenever a schema-affecting pull request lands — a frozen spec is only accurate for as long as nothing changes.

Frequently Asked Questions

Do PMs need to learn OpenAPI or Swagger to write a good API spec?

No — you can specify a complete contract in plain language and tables without touching YAML. Learning to read an OpenAPI document well enough to sanity-check what engineering built is genuinely useful; writing one yourself isn't part of the PM job at most companies.

What's the difference between an API spec and a PRD?

A PRD covers the whole feature — user problem, success metrics, UX flow, and every system involved — while an API spec is the narrow technical contract for one interface inside that feature. Most API requirements should live as a section inside the broader PRD, not as a freestanding, disconnected document.

How much detail is too much detail in an API requirement?

If you're specifying something with more than one reasonable engineering solution and the business genuinely doesn't care which one wins — pagination mechanics, endpoint naming, index strategy — you've gone one layer too deep. State the constraint and let engineering choose the mechanism.

Who decides error codes and status codes — PM or engineering?

Engineering owns the HTTP status code mapping (deciding between 404, 400, or 422 semantics), but PM owns which error conditions exist and what the user should experience for each one. Hand over the full list of "things that can go wrong" and let engineering assign the codes.

Should acceptance criteria be written before or after engineering scopes the API?

Write a first draft before technical scoping, covering the happy path and the error states you already know about, then revise it together with engineering during scoping. They'll surface edge cases — race conditions, retries, idempotency — that a product-only draft is likely to miss on its own.