Kokil Thapa - Professional Web Developer in Nepal
Freelancer Web Developer in Nepal with 15+ Years of Experience

Kokil Thapa is an experienced full-stack web developer focused on building fast, secure, and scalable web applications. He helps businesses and individuals create SEO-friendly, user-focused digital platforms designed for long-term growth.

API Documentation with Scribe for Laravel

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.

  1. 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
  2. Publish configuration: Generate the config file and base assets.
    php artisan scribe:install
    This creates config/scribe.php, a .scribe/ directory for custom overrides, and registers the service provider. Do not skip reviewing config/scribe.php; the defaults are sensible but rarely perfect for complex APIs.
  3. Set output formats: In config/scribe.php, enable the outputs your consumers actually need. Most teams require at least html (for browsing) and openapi (for codegen and Swagger UI).
    'output_formats' => [
        'html',
        'openapi',
        'postman',
    ],
  4. 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.

composer requireknuckleswtf/scribescribe:installPublish config & assetsEdit scribe.phpAuth & output formatsGenerate Docsartisan scribe:generate
Sequential setup flow for API documentation with Scribe for Laravel on PHP 8.2+

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.

@response / @bodyParam TagsHighest priority • Explicit overrideForm Request Validation RulesAuto-extracted types & constraintsType Hints & ReflectionFallback inference • Lowest priorityGenerated OutputOpenAPI Spec + HTML + PostmanMerged from all three sourcesHigher-priority sources win conflicts
Extraction priority chain determining final parameter and response shapes in generated docs

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.

CriteriaScribeSwagger-PHP (zircote)Scramble
Source of truthDocblocks + Form Requests + reflectionPure OpenAPI annotations/attributesCode-first inference (minimal annotations)
Laravel integration depthDeep (Form Requests, policies, resources)Generic PHP (no Laravel awareness)Deep (auto-infers from types & validation)
Maintenance burdenLow (reuses existing validation)High (duplicate spec alongside code)Very low (zero annotation ideal)
Response example controlExplicit @response tags or factoriesManual annotation onlyAuto-generated from Eloquent/API Resources
OpenAPI 3.1 supportPartial (3.0 primary, 3.1 beta)Full 3.1Full 3.1
Best forTeams with strong Form Request disciplineCross-framework shops needing pure OpenAPIGreenfield 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.

Existing Laravel codebase?YesNo (greenfield)Uses Form Requests?ScrambleYesNoScribeSwagger-PHPReuses validation rulesFramework-agnostic OpenAPIZero-config inference
Practical decision framework for selecting API documentation with Scribe for Laravel versus alternatives

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 the routes array 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 @response tags, Scribe may call the resource with null/fake data producing empty objects. Either add explicit response examples or configure example_models in config to use factory-created instances.
  • Authentication failures in examples: Verify auth.use_value resolves to a valid token in your current environment. Run php artisan tinker and 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 @bodyParam tags for complex nested structures instead of relying on auto-extraction.
  • Stale cache after changes: Always run scribe:generate --force after 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.

Frequently Asked Questions

Scribe is a Laravel package that generates interactive API docs from code annotations, config files, and response examples without requiring external services or manual markdown maintenance.

Scribe is free and open-source under MIT license. Paid tiers exist only for hosted cloud documentation; self-hosted static HTML generation remains completely free for unlimited commercial use.

Scribe integrates natively with Laravel routing and validation rules, reducing boilerplate compared to generic OpenAPI tools, though Swagger offers broader language support outside the PHP ecosystem.

Run composer require --dev knuckleswtf/scribe then php artisan scribe:install. This publishes the config file to config/scribe.php and creates a base documentation route at /docs. Ensure your application runs PHP 8.2 or higher as required by Laravel 12. The installation wizard asks whether you want static HTML, Postman collection, or OpenAPI spec output formats.

Yes, Scribe parses Form Request classes referenced in controller method signatures and extracts validation rules, field types, and custom messages automatically. This eliminates duplicating validation logic in docblocks. If detection fails, verify the Form Request type-hint matches exactly and that rules are defined in the rules() method rather than inline in controllers. Complex nested array validation may still require manual annotation overrides using @bodyParam tags.

Configure auth strategies in config/scribe.php under the auth key. For Sanctum token-based APIs common in Laravel projects I have built, set type to bearer and provide sample tokens in the example section. Scribe uses these values when generating request examples but never exposes real credentials. You can also define per-endpoint authentication requirements using @authenticated annotations in controller docblocks to override global defaults for specific routes like public health checks.

Scribe fully supports resource controllers and resolves route model binding parameters automatically. It detects Eloquent model types from type-hints and generates appropriate path parameter documentation including expected formats. When working on legal-tech portals with complex nested resources, I have found that explicitly naming parameters in route definitions improves clarity over relying solely on implicit binding names. Use @urlParam annotations if auto-detection produces incorrect descriptions or misses custom key fields.

Use Scribe response strategies to return fake data during generation. The faker strategy generates realistic payloads based on return types, while the database strategy queries actual records safely. For payment integrations like eSewa or Khalti where live calls are inappropriate during doc generation, configure custom response factories returning sanitized sample responses. Define these in config/scribe.php under response_strategies. This keeps documentation accurate without triggering real transactions or exposing sensitive third-party sandbox credentials.

Scribe ships with default themes but supports full customization through Blade views published via php artisan vendor:publish --tag=scribe-views. Modify resources/views/vendor/scribe/ to adjust navigation, styling, or content structure. On client projects requiring branded documentation portals, I typically extend the base template rather than replacing it entirely to preserve upgrade compatibility. Custom CSS can be added through the assets configuration key. Remember that major version upgrades may introduce breaking view changes, so pin your Scribe version in composer.json.

Group endpoints by version using route prefixes or dedicated documentation groups in config/scribe.php. Define separate group configurations pointing to different route patterns like api/v1/ and api/v2/. Each group generates its own documentation section with independent settings. For REST APIs serving multiple consumer versions simultaneously, this approach maintains clear separation without mixing deprecated and current endpoints. Version-specific authentication or base URL differences can be configured per group, which proves essential when maintaining legacy integrations alongside newer implementations.

Empty responses usually indicate failed response strategy execution or missing return type declarations. Check storage/logs/laravel.log for exceptions during php artisan scribe:generate. Common causes include unresolvable dependencies in controller constructors, missing environment variables needed for faker seeding, or response strategies attempting database queries against empty tables. Add @response annotations with explicit JSON payloads as fallbacks for problematic endpoints. Verify that test databases contain seed data if using the database response strategy, as Scribe does not run migrations automatically during generation.

Add php artisan scribe:generate to your GitLab CI pipeline before the Deployer 7 deploy task. Commit generated docs to the repository or pass them as artifacts to the deployment stage. On sister sites sharing deployment infrastructure, I generate documentation during the build phase and include it in the release artifact bundle. Configure Scribe to output static HTML to public/docs so Nginx serves it directly without PHP overhead. Cache Composer dependencies and Scribe's internal cache between pipeline runs to reduce generation time significantly.

Yes, enable postman collection output in config/scribe.php by adding postman to the output_formats array. Scribe generates a complete Postman collection JSON file with environments, authentication presets, and example requests matching your HTML documentation. Import this directly into Postman for immediate testing. Keep both outputs synchronized by running single generation commands rather than separate processes. For teams collaborating across frontend and backend roles, providing both formats reduces onboarding friction and ensures testers work against current endpoint specifications without manual translation errors.

Use @bodyParam annotations with type file for upload fields. Specify accepted MIME types and size limits in the description since Scribe cannot infer these from validation rules alone. For multipart form submissions combining files with JSON metadata, declare each field separately rather than nesting. In production eCommerce systems handling product image uploads, I have found that explicitly documenting maximum dimensions and format restrictions prevents client confusion. Test generation with actual sample files placed in storage/app/scribe-uploads to verify examples render correctly without broken references.

Never expose internal endpoints, admin routes, or debug information in public documentation. Use group filtering in config/scribe.php to exclude sensitive route patterns. Strip real API keys, tokens, and customer data from all examples even when using development databases. Review generated output manually before deployment since automated strategies may leak unintended details through error messages or database records. Host documentation behind authentication for internal APIs using Laravel middleware applied to the docs route. Regularly audit published docs as part of security reviews since endpoint changes can inadvertently expose new attack surface information.

Share this article

Quick Contact Options
Choose how you want to connect me: