Standards for designing APIs and command-line interfaces in DevRail-managed projects. These complement the Coding Practices and Release & Versioning standards.
Version APIs explicitly. Consumers must be able to depend on stable behavior within a version:
| Approach | Format | When to Use |
|---|---|---|
| URL path (preferred) | /v1/users, /v2/users |
REST APIs, most services |
| Header-based | Accept: application/vnd.myapp.v1+json |
When URL aesthetics matter or proxying is complex |
- Every public API is versioned from day one. There is no unversioned API.
- Never break existing clients without a version bump. Adding fields is safe. Removing, renaming, or changing field types requires a new version.
- Support at least two concurrent versions during migration. Document the deprecation timeline.
- Version the contract, not the implementation. Internal refactors do not require a version bump if the external behavior is unchanged.
All APIs use JSON for request and response bodies unless there is a specific reason not to (e.g., file uploads, streaming binary data).
Use a consistent envelope structure:
{
"data": { ... },
"meta": {
"request_id": "abc-123",
"timestamp": "2026-02-25T10:00:00Z"
}
}- Use
camelCasefor JSON field names in APIs that serve web clients. Usesnake_caseif the primary consumer is Python or the team convention prefers it. Be consistent within a project. - Include a request ID in every response for traceability.
- Use ISO 8601 for timestamps. Always include timezone (
Zor+00:00). - Nullable fields are explicit. If a field can be
null, document it. If a field is absent, it means "not provided" -- not the same asnull.
{
"error": {
"code": "VALIDATION_ERROR",
"message": "The request body is invalid.",
"details": [
{
"field": "email",
"issue": "Must be a valid email address."
}
]
}
}Use status codes correctly:
| Code | Meaning | When |
|---|---|---|
200 |
OK | Successful read or update |
201 |
Created | Successful resource creation |
204 |
No Content | Successful deletion, no body returned |
400 |
Bad Request | Validation error, malformed input |
401 |
Unauthorized | Missing or invalid authentication |
403 |
Forbidden | Authenticated but not authorized |
404 |
Not Found | Resource does not exist |
409 |
Conflict | Duplicate resource, concurrent modification |
422 |
Unprocessable Entity | Valid syntax but semantic error |
429 |
Too Many Requests | Rate limit exceeded |
500 |
Internal Server Error | Unexpected server failure |
- Never return
200with an error body. Use appropriate 4xx/5xx status codes. - Always include a machine-readable error code.
codeis for programmatic handling;messageis for humans. - Validation errors list all issues. Do not stop at the first invalid field -- return all validation failures at once.
- Never expose stack traces or internal details in error responses. Log them server-side.
- Every command supports
--help. This is non-negotiable. --helpoutput follows standard format: usage line, description, options with defaults, examples.- Use subcommands for complex CLIs.
mytool users list,mytool users create-- notmytool --list-users.
| Code | Meaning |
|---|---|
0 |
Success |
1 |
General error (runtime failure, operation failed) |
2 |
Misuse (invalid arguments, missing required flags) |
| Audience | Format | Flag |
|---|---|---|
| Humans (default) | Tables, colored text, progress bars | (default) |
| Machines | JSON | --output json or --json |
- Structured output for machines, readable output for humans. Support both with a flag.
- Write data to stdout, status to stderr. This allows piping output to other tools.
- No interactive prompts in non-TTY mode. If stdin is not a terminal, fail with a clear error rather than hanging on a prompt.
- Support
--quietand--verboseflags for controlling output verbosity.
- Adding a new optional field to a response
- Adding a new endpoint
- Adding a new optional query parameter
- Relaxing a validation constraint (accepting more inputs)
- Removing or renaming a field
- Changing a field's type
- Adding a new required field to a request
- Changing the semantic meaning of an existing field
- Changing error codes or response structures
- Removing an endpoint
- Tightening a validation constraint (rejecting previously valid inputs)
- Mark as deprecated in documentation and response headers (
Deprecation: true,Sunset: <date>). - Log usage of deprecated features to track migration progress.
- Provide a migration guide with specific instructions for updating to the new version.
- Remove after the documented sunset date and at least one major version bump.
- OpenAPI/Swagger specification is required for all REST APIs. The spec is the source of truth.
- Keep the spec in sync with the code. Generate from code annotations or validate the spec against the implementation in CI.
- Include examples for every endpoint -- request body, response body, error response.
- Document authentication requirements for each endpoint.
--helpis the primary documentation. It must be accurate and complete.- README includes common usage examples. Show the 3-5 most common workflows.
- Man pages are recommended for CLIs distributed via package managers.
For endpoints that return collections:
| Parameter | Purpose | Example |
|---|---|---|
page / offset |
Position in the result set | ?page=2 or ?offset=20 |
limit / per_page |
Number of items per page | ?limit=50 |
Include pagination metadata in the response:
{
"data": [ ... ],
"meta": {
"page": 2,
"per_page": 20,
"total": 150,
"total_pages": 8
}
}- Use query parameters for filtering:
?status=active&role=admin - Use a
sortparameter with field and direction:?sort=created_at:desc - Document all supported filter fields and sort options
- Return rate limit headers on every response:
X-RateLimit-Limit-- requests allowed per windowX-RateLimit-Remaining-- requests remaining in current windowX-RateLimit-Reset-- timestamp when the window resets
- Return
429 Too Many Requestswhen the limit is exceeded, with aRetry-Afterheader. - Document rate limits per endpoint or endpoint group.
- These standards apply to APIs and CLIs intended for external or cross-team consumption. Internal helper scripts and one-off tools have more flexibility.
- For language-specific API framework conventions (FastAPI, Flask, Express), refer to the relevant language standards.
- API design decisions (field naming convention, pagination style, authentication method) should be recorded in an ADR for the project.