API Error Response Contracts

API error response contracts define consistent, machine-readable error payloads and specify how those payloads relate to HTTP status codes. They make failures predictable for clients and can be expressed and validated through OpenAPI schemas.

An error contract describes the structure and meaning of an unsuccessful API response. A useful contract typically includes a stable machine-readable error code, a human-readable message, and optional structured details such as field-level validation errors or a request identifier.

HTTP status codes communicate the broad outcome, while the response body provides application-specific information. For example, `400` can indicate an invalid request, while an error code such as `invalid_email` identifies the particular application error. Clients should not have to parse human-readable messages to determine program behavior.

The schema should be stable enough that clients can depend on its required fields and documented types. In OpenAPI, reusable error schemas can be defined once and referenced from multiple responses, reducing drift between endpoints.

Different failure categories can share a common envelope while adding category-specific details. Validation failures might contain a list of invalid fields, whereas authentication failures may need only an error code and message. The contract should document which fields are guaranteed, optional, or conditionally present.

Error contracts should also avoid exposing implementation details such as stack traces, SQL statements, internal exception names, or sensitive data. Server-side logs can retain diagnostic information while the public response remains intentionally limited.

Key points

Interview questions

Why should an API use an error code in addition to a human-readable message?

A human-readable message is intended for people and may change as wording or localization changes. A stable error code gives clients a predictable value for programmatic handling. For example, a client can react to `invalid_email` without depending on the exact message text. This separation also allows the server to improve user-facing wording without breaking clients.

How do HTTP status codes and API error bodies work together?

The HTTP status code communicates the broad class of the outcome using standardized HTTP semantics. The error body adds application-specific information such as a stable code, message, or validation details. For example, a malformed request might use `400` with an `invalid_request` error code. Clients can use the status for generic handling and the error code for more specific behavior.

How would you design a reusable error schema for several API endpoints?

I would define a common error envelope containing fields such as `code`, `message`, and possibly `request_id`. Endpoint-specific failures could add structured details without changing the meaning of the common fields. In OpenAPI, the base schema can be reused with references so that multiple responses share the same contract. I would document required and optional fields and ensure the schema matches actual serialized responses.

What should an API return for unexpected server errors?

The API should return an appropriate server-error status, commonly `500` when no more specific status applies, together with a stable public error shape. The response should avoid exposing stack traces, database errors, or other implementation details. A request or correlation identifier can help operators connect the response with server-side logs. Detailed diagnostics belong in controlled logging and observability systems rather than the public response.

How can OpenAPI help prevent API error contracts from drifting?

OpenAPI can define reusable schemas and associate them with documented HTTP responses. Endpoints can reference the same error schema instead of independently describing similar payloads. Tooling can then validate specifications and, depending on the development workflow, validate or generate client and server artifacts from the contract. The important part is keeping the specification synchronized with the implementation.