River
Y CombinatorBacked by Y Combinator

Product & DesignFree

API Feature Spec With Failure Cases

Every step of the flow gets crossed against the partner's documented failures, and each cell that needs a decision becomes a row.

Start here

River reads the partner's documentation rather than asking you to summarise it. Their endpoints, their error catalogue, their rate limits and their webhook guarantees go in alongside the use cases you want to support. What comes back is a capability spec with the flow written out step by step, and a register that crosses every step against every way that step can fail. Each cell that still needs somebody to decide what happens becomes its own row.

The failure half is omitted because it looks like engineering detail, and it is not. Consider one call that times out with no response. HTTP's own specification is explicit that a client should not automatically retry a non-idempotent request without a way to know the original never applied. So somebody has to decide what the user sees while that is unresolved, which is a product question wearing a protocol costume. The templates ranking for this query give you a heading called Error Handling and a blank paragraph under it.

Built for the product manager writing the spec that engineering will estimate, and for the one who has watched a two-week integration take nine. Reach for it after the use cases are agreed and before anyone opens a ticket. The requirements it starts from come out of stakeholder inputs, the effort side gets priced by the feasibility brief, and the whole thing lands inside the PRD template as the section engineering reads first.

A developer and a product manager reading API documentation together at a laptop
Built for the day after the use cases are agreed and before the integration ticket is opened.

Where a two-week integration goes

Ridgeline pushes completed jobs into a customer's accounting system as invoices. The flow is six steps: connect, read the accounts and tax rates, map the customer, create the invoice, take payment status back, and reverse it when a job is cancelled after invoicing. The partner documents eight ways any of those can fail. Crossing the two gives forty-eight cells, of which twenty-nine actually apply. That is almost five failure cases for every step of the happy path.

The draft spec answered six of the twenty-nine, all of them the same one: retry with backoff when the partner rate-limits. That is the easy cell, and it is not even fully settled, since the specification that defines the rate-limit response only says a server may include a hint about how long to wait. Twenty-three cells are open. Eleven are mechanical and engineering can settle them. Seven need a product call. Five cannot be undone once they go wrong.

The second half of the effort is the data. Creating one invoice at the partner requires thirty-four fields. Eighteen already sit on a Ridgeline job and seven can be computed from what is there. Nine do not exist anywhere in the product. Four are read from the partner at connect time, three become a new per-customer setting, and two become a new field on the job form, which means a migration and a change to a screen every technician uses daily.

How it works

  1. Read the partner docs

    Their endpoints, error catalogue, rate limits, pagination rules and whatever they guarantee about webhook delivery.

  2. Write the flow

    Each step stated as a call, with what goes out and what comes back.

  3. Cross the grid

    Every step against every failure, keeping only the cells that can actually occur.

  4. Decide or escalate

    Each cell either gets an answer in the spec or a named owner and a deadline.

What you get

  • The flow written as steps, each one a call the integration actually makes
  • Every step crossed against every documented failure, with the cells that do not apply removed
  • Each open cell as a row: what happens, what the user sees, who decides
  • Failures separated into mechanical, product call, and the ones that cannot be undone
  • The field register: everything the partner requires, and what your product does not hold
  • Missing fields sorted by what they cost, from a lookup to a migration
  • Rate limits and pagination read out of the partner's own documentation, not assumed

Common questions

Why not just let engineering handle the failure cases?

Because most of them are not engineering questions. Whether a rejected invoice leaves the job marked complete, and whether the technician who closed it finds out, are product decisions with no technically correct answer. Engineering will decide them anyway if the spec is silent, in whatever way is quickest to build, and you will meet the result in feedback triage three months later.

Twenty-nine cells sounds like a lot of spec.

Eleven of them get one line each, because the answer is retry and nobody needs to read it twice. Seven need a paragraph. Five need a paragraph and a named owner. The document is not long, it is just complete in the half that usually says error handling to be confirmed.

What if the partner's documentation is bad?

Then that is the first finding, and it is worth more than the spec. A partner who does not publish their error codes, their rate limits or their webhook delivery guarantee has handed you an unbounded estimate. The register lists exactly which questions to send them, which is a better email than asking whether the API is any good.

Does it write the API contract itself?

It writes the capability spec and the field register, not the schema. The register names every field the partner requires, where each one comes from in your product, and the nine that come from nowhere. That is what engineering needs to scope. The schema follows once the missing nine have owners.

What makes a failure case irreversible?

Money moved, or a document now exists in somebody else's ledger that you cannot unmake. A timeout on an invoice create is irreversible in that sense: you do not know whether their accountant is looking at it. Those five cells are the ones that need reconciliation state the happy path never needed, which is where the effort actually is.

Does this work for an API we are publishing, not consuming?

Yes, with the grid turned around. The steps become the calls a client makes against you, and the failure classes become the states you have to define and document. The output is then the error catalogue you will publish, which is the artifact your own integrators will be crossing against their flow. Retiring a version of it later runs off the same register.

How does this turn into stories?

One cell rarely maps to one story, and the register makes that visible: three of the five irreversible cells sit on a single step, so they become one story about reconciliation rather than three. Feed the register into stories with acceptance criteria and each open cell arrives as an edge case with an owner already attached.

API Feature Spec With Failure Cases

Fill in the form and your workspace opens with the work already underway.