
An API specification can list every URL and still leave the hard questions unanswered. What happens if a payment request times out after the provider has charged the card? Which system owns a customer's address? Will an old mobile app keep working after the next release?
Describe behavior at the edges
For each operation, define the request, response, validation rules, and permission checks. Then define the failures: invalid input, unavailable dependency, duplicate request, and a partial success. Error messages need stable codes for software and plain explanations for people. A generic 500 response forces every caller to guess.
For operations that can be retried, use an idempotency key or another explicit duplicate-handling rule. A timeout tells the caller that the answer did not arrive; it does not prove the operation failed. This matters for payments, orders, and anything that sends a message.
Give every field an owner
If two systems can both edit the same field, define which one wins and when updates move between them. We draw this as a small table before writing integration code. It exposes conflicts that a JSON example cannot show.
Finally, plan how the contract changes. Add fields without breaking existing clients, deprecate deliberately, and log which versions are still in use. Contract tests can then check behavior on both sides of the integration. The goal is an API that a second team can use without a private chat with its author.

