The challenge of keeping API docs in sync
Documentation drift is one of the most frustrating problems in API development. Your endpoint works perfectly, but the docs describe a parameter that no longer exists, or they're missing the new authentication header your team added last sprint. API documentation for Laravel: best practices explained means understanding that docs aren't afterthoughts—they're part of your contract with consumers, and they need to stay accurate automatically.
In this guide, we'll walk through the tools and patterns that make this happen. We'll cover two major documentation generators, how to properly document authentication and errors, and why publishing docs with every release matters for your team's credibility and developer experience.
Why Laravel teams struggle with API documentation
Laravel developers often face a choice: write docs manually and watch them decay, or spend engineering time building a custom solution. The manual approach creates technical debt quickly. A teammate updates an endpoint's response structure, forgets to update the wiki, and six months later a mobile team wastes a day debugging against outdated specs.
The solution is automation. Modern Laravel tooling can generate accurate documentation directly from your code, treating docs as a first-class artifact that reflects reality.
Scribe: generating docs from your routes
Scribe (scribe.knuckles.wtf) is a Laravel package that extracts documentation from your routes and controller methods, then generates beautiful interactive docs. It works by reading your code—specifically your route definitions, controller docblocks, and request/response models.
Here's how to get started:
composer require knuckles/scribeAfter installation, you configure Scribe in config/scribe.php. The basic setup points to your routes and tells Scribe where to find your API endpoints.
Let's say you have an endpoint that lists products:
/**
* List all products
*
* Retrieves a paginated list of active products.
*
* @queryParam page integer The page number. Example: 1
* @queryParam per_page integer Results per page. Example: 15
* @queryParam status string Filter by status (active, archived). Example: active
* @response 200 {
* "data": [
* {
* "id": 1,
* "name": "Widget Pro",
* "price": 29.99,
* "stock": 145
* }
* ],
* "meta": {
* "current_page": 1,
* "total": 342
* }
* }
*/
public function index(Request $request)
{
return Product::query()
->where('status', $request->query('status', 'active'))
->paginate($request->query('per_page', 15));
}Scribe reads these docblock tags and generates interactive documentation. When you run php artisan scribe:generate, it outputs HTML files with request/response examples, query parameters documented, and even runnable examples in the browser.
The beauty of Scribe is its flexibility. It supports FormRequest classes, so you can leverage Laravel's validation rules for documentation:
class StoreProductRequest extends FormRequest
{
public function rules()
{
return [
'name' => 'required|string|max:255',
'price' => 'required|numeric|min:0.01',
'stock' => 'required|integer|min:0',
'category_id' => 'required|exists:categories,id',
];
}
}Scribe reads these rules and includes them in the generated docs automatically. No need to duplicate validation specs in docblocks.
Scramble: building OpenAPI from your codebase
Scramble (scramble.dedoc.co) takes a different approach. Instead of parsing docblocks, Scramble generates OpenAPI 3.0 specifications directly from your Laravel application's code and type hints. This is powerful because it works with PHP 8+ type declarations.
Install Scramble:
composer require dedoc/scrambleThen publish the configuration:
php artisan vendor:publish --provider="Dedoc\Scramble\ScrambleServiceProvider"Scramble shines when you use proper typing in your controllers and models. Here's an example:
use App\Http\Requests\CreateOrderRequest;
use App\Models\Order;
public function store(CreateOrderRequest $request): Order
{
return Order::create($request->validated());
}The return type : Order tells Scramble what the response looks like. Combined with your FormRequest rules, Scramble builds a complete OpenAPI spec.
You can access the spec at /api/documentation or download it as /api/openapi.json. This JSON file integrates with tools like Postman, Insomnia, or code generators that consume OpenAPI specs.
Scramble also generates beautiful HTML docs using Swagger UI, so developers get an interactive interface right in their browser.
Documenting authentication properly
Every API needs clear authentication documentation. Vague or missing auth docs lead to integration failures, support tickets, and frustrated developers. Whether you use API tokens, OAuth2, or JWTs, your docs must explain:
- How to obtain credentials
- Which endpoints require authentication
- The exact header or parameter format
- Token expiration and refresh behavior
- Common auth errors and their meanings
In Scribe, document auth with the @authenticated tag and the @header tag:
/**
* Get the current user
*
* Returns details for the authenticated user.
*
* @authenticated
* @header Authorization Bearer {YOUR_TOKEN}
* @response 200 {
* "id": 1,
* "name": "Alice Johnson",
* "email": "alice@example.com",
* "role": "admin"
* }
*/
public function me()
{
return auth()->user();
}In Scramble, leverage middleware to mark routes as requiring authentication:
Route::middleware('auth:sanctum')->group(function () {
Route::get('/me', [UserController::class, 'me']);
Route::post('/orders', [OrderController::class, 'store']);
});
Scramble detects this middleware and marks those endpoints as requiring authentication in the generated spec.
For API key authentication, document it explicitly:
/**
* List invoices
*
* @header X-API-Key {YOUR_API_KEY}
* @queryParam month string Filter by month (YYYY-MM). Example: 2024-01
*/
public function invoices(Request $request)
{
// Implementation
}Documenting errors and status codes
Developers need to know what can go wrong. Document error responses alongside success responses. In Scribe, use multiple @response tags:
/**
* Update a product
*
* @urlParam id integer The product ID. Example: 1
* @bodyParam name string The product name. Example: Widget Pro
* @bodyParam price numeric The selling price. Example: 29.99
*
* @response 200 {
* "id": 1,
* "name": "Widget Pro",
* "price": 29.99,
* "updated_at": "2024-01-15T10:30:00Z"
* }
*
* @response 404 {
* "message": "Product not found",
* "error_code": "PRODUCT_NOT_FOUND"
* }
*
* @response 422 {
* "message": "The given data was invalid",
* "errors": {
* "price": ["The price must be a number"]
* }
* }
*/
public function update(UpdateProductRequest $request, Product $product)
{
$product->update($request->validated());
return $product;
}This shows consumers exactly what they'll receive in success and failure scenarios. Include error codes so they can handle specific errors programmatically.
With Scramble, leverage Laravel's exception handling to document errors. Use resource classes to shape your error responses consistently, and Scramble will include them in the generated docs.
Publishing documentation with every release
Docs that go stale are worse than no docs. The solution is automation: generate and publish docs as part of your deployment pipeline.
For Scribe, add this to your CI/CD workflow (GitHub Actions example):
- name: Generate API docs
run: php artisan scribe:generate
- name: Commit and push docs
run: |
git config --global user.email "ci@example.com"
git config --global user.name "CI Bot"
git add docs/
git commit -m "docs: update API documentation"
git push
Alternatively, host generated docs on a static site. Many teams upload Scribe's output to AWS S3 or GitHub Pages, then link to it from their API portal.
For Scramble, the OpenAPI JSON is generated on the fly, so it's always current. Version your OpenAPI spec by including your API version in the metadata:
// config/scramble.php
'info' => [
'title' => 'Your API',
'version' => '2.0.0', // Update with each release
],
Then version your generated spec: /api/v2/openapi.json. Consumers can always grab the latest spec, but they can also reference a pinned version if they need stability.
Common mistakes
Underestimating the role of examples. Generic documentation is forgettable. Include concrete examples with realistic data. Show pagination, filtering, sorting—the real-world queries developers will write.
Leaving errors undocumented. Developers will hit errors. If your docs don't cover them, they'll waste time debugging. Document all error responses, not just success cases.
Not versioning your docs. When you release a breaking change, old API versions still have clients. Host multiple versions of your docs (e.g., /docs/v1, /docs/v2) so developers know exactly what their version supports.
Forgetting that docs are part of code review. If you change an endpoint's behavior, the docs change too. Make documentation updates part of your PR checklist, just like tests.
Skipping authentication documentation. "Just add the Authorization header" isn't good enough. Spell out the exact format, provide examples, and explain token expiration.
Key takeaways
- Automate documentation generation with Scribe (docblock-based) or Scramble (type-hint-based) to keep docs in sync with your code.
- Document authentication clearly: explain how to obtain credentials, the exact header format, and token lifecycle.
- Include error responses alongside success responses, with error codes that developers can handle programmatically.
- Generate and publish docs with every release—use CI/CD to make this automatic so docs never decay.
- Treat documentation as a first-class artifact: review it in PRs, version it with your API, and host multiple versions for breaking changes.
- Include concrete, realistic examples with pagination, filtering, and other real-world scenarios consumers will use.






Comments
Loading comments…