
August 14, 2026
9 min read
Table of Contents
By Kokil Thapa | Last reviewed: August 2026
Shipping a REST API without reliable documentation creates immediate friction for frontend teams, mobile developers, and third-party integrators. API documentation with Scribe for Laravel solves this by extracting endpoint definitions, validation rules, and response examples directly from your PHP 8.2+ application code rather than maintaining separate YAML files that drift out of sync. This approach ensures your published docs always match the actual deployed behaviour of your Laravel 12.x or 11.x backend.
How do you install and configure Scribe for Laravel API documentation?
Before generating anything, you need a correctly configured environment. On any production-grade Laravel project I maintain, I treat documentation tooling as a dev-dependency, never shipping it to production servers. For context on structuring the underlying endpoints themselves, refer to these Laravel API best practices before layering on documentation generation.
- Install via Composer: Require the package as a development dependency. Scribe 4.x is the current stable line compatible with Laravel 11 and 12 on PHP 8.2, 8.3, and 8.4.
composer require --dev knuckleswtf/scribe:^4.0 - Publish configuration: Generate the config file and base assets.
This createsphp artisan scribe:installconfig/scribe.php, a.scribe/directory for custom overrides, and registers the service provider. Do not skip reviewingconfig/scribe.php; the defaults are sensible but rarely perfect for complex APIs. - Set output formats: In
config/scribe.php, enable the outputs your consumers actually need. Most teams require at leasthtml(for browsing) andopenapi(for codegen and Swagger UI).'output_formats' => [ 'html', 'openapi', 'postman', ], - Configure authentication: If your API uses Laravel Sanctum (the default for new Laravel 12 projects), tell Scribe how to authenticate example requests. Without this, generated examples will show 401 responses.
'auth' => [ 'enabled' => true, 'default' => true, 'in' => 'bearer', 'name' => 'Authorization', 'use_value' => env('SCRIBE_AUTH_KEY'), 'placeholder' => '{YOUR_AUTH_TOKEN}', ],
A common mistake on client projects is leaving use_value hardcoded in the committed config. Always pull tokens from environment variables so each developer and CI runner can use their own test credentials without leaking secrets into version control.
How does Scribe extract endpoint details from Laravel controllers and Form Requests?
Scribe’s primary advantage over manual OpenAPI authoring is its extraction strategy. It reads three sources in priority order: dedicated @response / @bodyParam docblock tags, Laravel Form Request validation rules, and finally inferred type hints. Understanding this hierarchy prevents confusion when generated output doesn’t match expectations.
Leveraging Form Requests for automatic parameter documentation
If your controller methods already use Form Requests (and they should, per standard modern Laravel architecture best practices), Scribe extracts field names, types, required status, and validation messages automatically. A rule like 'email' => 'required|email|max:255' becomes a documented string parameter marked required with max-length metadata. No extra annotations needed.
// app/Http/Requests/StoreLeadRequest.php
public function rules(): array
{
return [
'full_name' => ['required', 'string', 'max:100'],
'email' => ['required', 'email'],
'service_id' => ['required', 'exists:services,id'],
'message' => ['nullable', 'string', 'max:2000'],
];
} Adding response examples without hitting the database
Real database calls during doc generation are fragile and slow. Use @response tags with inline JSON to define canonical success and error shapes. These override live responses and guarantee consistent output regardless of seed data state.
/**
* Create a new legal service lead.
*
* @bodyParam full_name string required Max 100 chars. Example: "Ram Thapa"
* @bodyParam email email required Example: ram@example.com
* @bodyParam service_id integer required Must exist in services table. Example: 5
* @bodyParam message string nullable Optional inquiry details.
*
* @response 201 {"id": 42, "full_name": "Ram Thapa", "status": "pending"}
* @response 422 {"message": "The email has already been taken.", "errors": {"email": ["..."]}}
*/
public function store(StoreLeadRequest $request): JsonResponse In my experience building legal-tech portals like Notary Nepal and Court Marriage In Nepal, this pattern eliminates an entire class of bugs where documentation shows outdated field names after a migration renamed columns but nobody updated the YAML spec. The docs regenerate from the same source of truth the application validates against.
How do you generate OpenAPI specs and integrate them into CI/CD pipelines?
Documentation that isn't regenerated on every deploy is already stale. Treat generation as a build artefact, not a manual task. On sister sites sharing a Deployer 7 + GitLab CI pipeline, I enforce regeneration in the CI job before deployment proceeds.
Local and CI generation commands
# Standard generation (respects config/scribe.php)
php artisan scribe:generate
# Force regeneration ignoring cached .scribe/endpoints.cache.json
php artisan scribe:generate --force
# Generate only OpenAPI spec (faster for CI validation)
php artisan scribe:generate --format openapi GitLab CI integration example
Add a documentation stage that fails the pipeline if generated output differs from what's committed. This catches developers who update routes but forget to regenerate.
docs-check:
stage: validate
image: php:8.4-cli
script:
- composer install --no-interaction --prefer-dist
- php artisan scribe:generate --force
- git diff --exit-code public/docs
artifacts:
paths:
- public/docs/openapi.yaml
expire_in: 1 week The --exit-code flag makes git diff return non-zero if any file changed, failing the job immediately. This single check has prevented more stale-doc incidents on my projects than any amount of team reminders or PR checklist items.
Serving generated documentation securely
Scribe outputs static files to public/docs/ by default. In production, serve these through Nginx/Apache as static assets — never route them through PHP. For authenticated-only APIs, protect the docs path with IP whitelisting or basic auth at the web-server level. Exposing internal API structure publicly invites reconnaissance; I've seen this become a security audit finding on two separate client engagements.
What are the practical differences between Scribe, Swagger-PHP, and Scramble for Laravel?
Choosing a documentation tool depends on your team's workflow, not feature checklists. Here's how these three compare in real Laravel 12 projects running PHP 8.2+ as of 2026.
| Criteria | Scribe | Swagger-PHP (zircote) | Scramble |
|---|---|---|---|
| Source of truth | Docblocks + Form Requests + reflection | Pure OpenAPI annotations/attributes | Code-first inference (minimal annotations) |
| Laravel integration depth | Deep (Form Requests, policies, resources) | Generic PHP (no Laravel awareness) | Deep (auto-infers from types & validation) |
| Maintenance burden | Low (reuses existing validation) | High (duplicate spec alongside code) | Very low (zero annotation ideal) |
| Response example control | Explicit @response tags or factories | Manual annotation only | Auto-generated from Eloquent/API Resources |
| OpenAPI 3.1 support | Partial (3.0 primary, 3.1 beta) | Full 3.1 | Full 3.1 |
| Best for | Teams with strong Form Request discipline | Cross-framework shops needing pure OpenAPI | Greenfield Laravel APIs wanting zero overhead |
In practice, I default to Scribe for existing Laravel applications because most already have Form Requests. The marginal cost of adding @response tags is far lower than retrofitting full Swagger attributes across hundreds of endpoints. For brand-new Laravel 12 APIs where I control the architecture from day one, Scramble's inference engine is compelling — but Scribe's explicit control still wins for client-facing legal-tech portals where every documented field must be auditable and intentional.
How do you troubleshoot missing endpoints and incorrect response examples in Scribe?
Even with correct configuration, Scribe occasionally misses routes or generates wrong examples. These are the recurring issues I encounter and their fixes.
- Endpoints missing entirely: Scribe only documents routes assigned to groups defined in
config/scribe.php. Check that your route file uses the correct middleware group or prefix matching theroutesarray in config. Routes registered conditionally (e.g., behind feature flags) won't appear unless the flag is active during generation. - Wrong response shape: If you're using API Resources but haven't added
@responsetags, Scribe may call the resource with null/fake data producing empty objects. Either add explicit response examples or configureexample_modelsin config to use factory-created instances. - Authentication failures in examples: Verify
auth.use_valueresolves to a valid token in your current environment. Runphp artisan tinkerand test the token manually. Expired Sanctum tokens are the #1 cause here. - Nested validation rules ignored: Scribe 4.x handles dot-notation rules (
'items.*.price') but struggles with deeply nested array validation. Add explicit@bodyParamtags for complex nested structures instead of relying on auto-extraction. - Stale cache after changes: Always run
scribe:generate --forceafter modifying docblocks or Form Requests. The endpoint cache persists across runs for speed but doesn't invalidate on file changes reliably.
For teams working on RESTful API development in Laravel, establishing a convention of regenerating docs locally before pushing prevents most of these issues from reaching code review. Make it part of your pre-commit hook or local workflow muscle memory.
Implementing sustainable API documentation with Scribe for Laravel workflows
Reliable API documentation with Scribe for Laravel comes from treating docs as a build artefact tied to your deployment pipeline, not an afterthought. Install it as a dev dependency, anchor extraction to Form Requests you'd write anyway, pin response examples explicitly, and enforce regeneration in CI. This approach has kept documentation accurate across multiple production Laravel systems I maintain — from legal service portals processing sensitive client intake to eCommerce platforms handling multi-currency transactions. If your team is struggling with stale specs or manual YAML maintenance, start with the installation steps above and integrate the CI check before attempting advanced customization. Need help setting up Scribe on an existing Laravel API or evaluating whether it fits your architecture? Get in touch to discuss your specific requirements.

