API Documentation with Scribe for Laravel

Khimananda Oli 9 min read Programming and Languages
API Documentation with Scribe for Laravel

By Khimananda Oli | Last reviewed: August 2026

Outdated API references break integrations faster than bad code, especially when frontend teams or external partners rely on your endpoints being correct. API documentation with Scribe for Laravel solves this by extracting metadata directly from your routes, controllers, and validation rules to produce living docs that stay synchronized with your application. This guide walks you through configuring Scribe, annotating endpoints effectively, and automating generation so your documentation never drifts from production behavior.

Laravel AppRoutes & ControllersValidation RulesResponse FactoriesScribe EngineRoute IntrospectionAnnotation ParsingResponse GenerationStatic HTML Docs/public/docsOpenAPI Specopenapi.yamlPostman Collectioncollection.json
Scribe extracts metadata from Laravel source code and generates multiple API documentation formats automatically

How do you install and configure API documentation with Scribe for Laravel?

Start by requiring Scribe as a dev dependency. Since documentation generation should never ship to production containers, keeping it out of the main dependency tree reduces image size and attack surface.

composer require --dev knuckleswtf/scribe

Publish the configuration file to customize extraction strategies, output formats, and authentication defaults. This is where most teams get stuck — they accept defaults and later wonder why certain fields are missing or misclassified.

php artisan vendor:publish --tag=scribe-config

Edit config/scribe.php to match your project structure. The three settings that matter most in practice are routes (which route groups to document), strategies (how metadata is extracted), and type (output format). For a typical REST API built with Laravel Sanctum authentication, set the auth type to bearer and define a default token for example requests:

'auth' => [
    'enabled' => true,
    'default' => true,
    'in' => 'bearer',
    'name' => 'Authorization',
    'use_value' => env('SCRIBE_AUTH_TOKEN'),
],

Never hardcode tokens. Use environment variables even in development configurations. When documenting APIs that handle sensitive data — common in Nepal's growing fintech sector where data protection compliance matters — ensure your example responses never expose real PII. Scribe supports faker-based generation, but always audit generated examples before publishing.

How do you annotate Laravel controllers for accurate API documentation?

Scribe uses PHPDoc annotations and Laravel's native validation rules as its primary data sources. The annotation layer supplements what cannot be inferred from code alone: human-readable descriptions, grouped categories, and edge-case response examples.

Grouping and describing endpoints

Use @group at the class level to organize endpoints logically. Individual methods use @unauthenticated when an endpoint does not require auth despite global defaults, and @responseField to describe nested JSON structures that Scribe cannot infer from resource classes.

/
 * @group User Management
 * Endpoints for managing user accounts and profiles.
 */
class UserController extends Controller
{
    /
     * Update user profile.
     *
     * Validates and updates the authenticated user's profile information.
     * Returns the updated user resource.
     *
     * @responseField data object The updated user resource.
     * @responseField data.name string Full name of the user.
     * @responseField data.email string Primary email address.
     * @responseField meta.updated_at string ISO 8601 timestamp of last update.
     */
    public function update(UpdateProfileRequest $request): UserResource
    {
        // ...
    }
}

Leveraging Form Requests for automatic parameter docs

This is where Scribe shines compared to manual documentation tools. If you use dedicated Form Request classes, Scribe extracts field names, types, validation rules, and custom messages without any additional annotation. A well-structured request class serves double duty: runtime validation and documentation source of truth.

class UpdateProfileRequest extends FormRequest
{
    public function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', Rule::unique('users')->ignore($this->user()->id)],
            'phone' => ['nullable', 'string', 'regex:/^9[0-9]{9}$/'],
        ];
    }

    public function messages(): array
    {
        return [
            'phone.regex' => 'Phone must be a valid Nepali mobile number.',
        ];
    }
}

Scribe converts these rules into parameter tables with types, required flags, and descriptions derived from message overrides. This eliminates the most common documentation failure mode: parameters documented in one place but validated differently in code.

Document EndpointHas Form Request + Resource?Auto-extract params & response shapeYesNoAnnotation OnlyAdd @responseField fornested/custom structuresResponse CallsExecute endpoint againsttest DB for real outputFast, no side effectsBest for stable APIsAccurate complex responsesRequires test environmentHybrid: Combine Both Strategies
Choose Scribe extraction strategies based on endpoint complexity and available test infrastructure

How do you generate realistic response examples without exposing production data?

Scribe offers two complementary approaches: response calls (executing the endpoint against a test database) and manual/faker responses (defined via annotations or factory models). In practice, use both strategically rather than picking one exclusively.

Configuring safe response calls

Response calls execute actual HTTP requests during generation. This produces perfectly accurate examples but requires a disposable test environment. Configure the database, seeders, and authentication state in scribe.php:

'response_calls' => [
    'methods' => ['GET', 'POST', 'PUT', 'PATCH'],
    'config' => [
        'app.env' => 'testing',
        'database.default' => 'sqlite',
        'database.connections.sqlite.database' => ':memory:',
    ],
    'migrate_db' => true,
    'seed_db' => true,
    'seeder_class' => DatabaseSeeder::class,
],

A common mistake is running response calls against a shared staging database. This creates flaky docs when concurrent tests modify data. Always use an isolated SQLite in-memory database or a dedicated test schema. For teams following Laravel testing with Pest in CI/CD, align your Scribe test configuration with your existing test suite to avoid maintaining parallel environments.

Defining manual response examples

For endpoints with side effects, external dependencies, or non-deterministic outputs, define explicit examples using @response annotations or Eloquent model factories. This is safer and faster than response calls for most write operations.

/**
 * @response 201 scenario="User created successfully"
 * {"data":{"id":42,"name":"Anita Sharma","email":"[email protected]"},"meta":{"created_at":"2026-08-17T10:30:00Z"}}
 *
 * @response 422 scenario="Validation failed"
 * {"message":"The email has already been taken.","errors":{"email":["The email has already been taken."]}}
 */

Document error responses explicitly. Developers integrating your API need to know what 422, 403, and 404 payloads look like — not just the happy path. Scribe renders each scenario separately in the generated HTML, making error handling discoverable rather than buried in source code.

How does Scribe compare to OpenAPI and Swagger for Laravel APIs?

Choosing a documentation tool depends on your team's workflow, not feature checklists. Here is how the main options compare for Laravel projects in 2026:

CriteriaScribeswagger-php (OpenAPI)Laravel Scramble
Setup effortLow — works with existing PHPDoc and Form RequestsHigh — requires dedicated OA attributes throughout codebaseVery low — zero annotations, pure inference
Output formatsHTML, OpenAPI, Postman, MarkdownOpenAPI spec only (rendered separately)HTML viewer only
Response example accuracyHigh — response calls + manual examplesManual only — no execution capabilityMedium — inferred from type hints and resources
Customization depthExtensive — custom strategies, themes, groupingFull OpenAPI spec controlLimited — opinionated defaults
CI/CD integrationNative artisan command, cache supportRequires separate generation + rendering pipelineRuntime generation, less suited for static hosting
Best forTeams wanting accurate multi-format docs with minimal annotation overheadOrganizations mandating OpenAPI as contract-first standardRapid prototyping and internal APIs with simple response shapes

Scribe occupies the practical middle ground: enough automation to reduce maintenance burden, enough control to handle complex APIs, and native Laravel integration that respects framework conventions. If your organization mandates OpenAPI as a design-first contract, use swagger-php. If you need fast internal docs with zero ceremony, try Scramble. For everything else — especially customer-facing APIs where accuracy matters — API documentation with Scribe for Laravel delivers the best balance.

How do you automate API documentation generation in CI/CD pipelines?

Documentation that requires manual regeneration becomes outdated within weeks. Automate generation as part of your deployment pipeline so every release ships with synchronized docs. This aligns with the principle that if documentation is not automated, observable, and audit-ready, it is not production-ready.

GitHub Actions example

Add a documentation job that runs after tests pass but before deployment. Cache the Scribe output to avoid regenerating unchanged endpoints:

docs:
  runs-on: ubuntu-latest
  needs: test
  steps:
    - uses: actions/checkout@v4
    - uses: shivammathur/setup-php@v2
      with:
        php-version: '8.4'
        extensions: mbstring, pdo_sqlite
    - run: composer install --no-interaction --prefer-dist
    - run: php artisan scribe:generate --force
    - uses: actions/upload-artifact@v4
      with:
        name: api-docs
        path: public/docs

The --force flag bypasses the interactive prompt. Store generated docs as artifacts or deploy them directly to your hosting target. For teams using GitLab CI for Laravel, adapt this pattern to GitLab's artifact and pages system.

Versioning and changelog tracking

Treat documentation as a versioned artifact. Tag doc releases alongside application versions. Scribe supports custom metadata injection — use this to embed build timestamps, git SHAs, or version numbers into generated output. When breaking changes occur, maintain parallel documentation versions rather than overwriting previous specs. External consumers need stable references even as your API evolves.

Code PushMain branch mergeTriggers pipelineTest SuitePest / PHPUnitMust pass firstScribe Generatephp artisan scribe:generate--force flagDeploy DocsUpload artifactPublish to hostingGate: Fail pipeline if Scribe errors or warnings detectedPrevents deploying undocumented or broken API changes
Automated Scribe generation in CI ensures API documentation stays synchronized with every deployment

Make API Documentation with Scribe for Laravel Part of Your Release Process

Accurate API documentation with Scribe for Laravel is not a nice-to-have — it is infrastructure that prevents integration failures, reduces support load, and signals engineering maturity to partners and customers. Install Scribe, configure extraction strategies that match your codebase conventions, automate generation in your CI pipeline, and treat documentation quality as a deployment gate rather than an afterthought. If your team needs help establishing documentation workflows that survive real-world development velocity, reach out to discuss your API documentation strategy.

Frequently Asked Questions

Run composer require knuckleswtf/scribe to add the package. Publish the configuration file using php artisan vendor:publish --tag=scribe-config. This setup works with Laravel 11 and 12 in 2026, generating docs directly from your route definitions and controller annotations without external dependencies.

Yes, Scribe is completely open source under the MIT license. There are no paid tiers, usage limits, or licensing fees for commercial API documentation generation in 2026. You can use it freely in proprietary Laravel applications without attribution requirements beyond standard composer metadata.

Scribe generates static HTML and Postman collections natively, while Swagger requires runtime JavaScript rendering. Scribe extracts examples from actual Laravel tests and attributes, reducing manual annotation overhead compared to OpenAPI spec writing required by Swagger UI for accurate endpoint documentation.

Yes, Scribe detects Laravel Sanctum, Passport, and JWT guards from route middleware. It documents token requirements, header formats, and auth flows based on your actual security configuration. Custom strategies handle proprietary auth schemes through dedicated strategy classes registered in the scribe.php config file.

Missing examples usually indicate absent test responses or @response attributes. Add #[Response] attributes to controller methods or ensure feature tests return valid JSON responses. Run php artisan scribe:generate with --verbose flag to identify which endpoints lack example data during the extraction phase.

Copy views from vendor/knuckleswtf/scribe/resources/views to resources/views/vendor/scribe. Modify Blade templates to match your brand colors and layout. The default 'default' theme supports CSS overrides via custom.css in public/docs directory without touching core template files.

Yes, Scribe introspects ApiResource and ResourceCollection classes to document transformed response structures. It executes resources against sample models during generation to capture actual output shapes. Ensure factories exist for all referenced models so Scribe can instantiate valid data for accurate schema documentation.

Regenerate docs in CI pipelines on every merge to main branch using php artisan scribe:generate. Treat documentation as build artifact, not manual task. Automate deployment to your docs hosting alongside application releases to prevent drift between code and published API reference in 2026 workflows.

Yes, configure multiple route groups in scribe.php targeting different version prefixes like /api/v1 and /api/v2. Use separate output directories or subdomains per version. Each group applies independent filtering rules, allowing parallel documentation maintenance for legacy and current API versions without cross-contamination.

Silent failures typically stem from uncaught exceptions during example generation or missing model factories. Enable debug mode in config and check storage/logs/scribe.log for stack traces. Common culprits include database connections failing during generation or policy checks blocking introspection of protected endpoints.

Add route patterns to exclude.routes array in scribe.php config. Use wildcard matching like admin. or internal/ to filter non-public endpoints. Alternatively, apply #[HideFromAPIDocumentation] attribute directly to controller methods for granular exclusion without modifying global configuration settings.

Yes, Scribe generation runs as CLI command independent of Octane runtime. However, avoid generating docs during Octane-served requests due to memory constraints. Schedule generation as separate artisan command in deployment scripts. Octane compatibility issues only arise if custom strategies depend on request-scoped services.

Generated docs are static HTML with no server-side execution risk. Restrict access via middleware or IP whitelisting if documenting sensitive internal APIs. Never expose .env values or real credentials in examples. Use fake data generators and sanitize any dynamic content before publishing documentation publicly.

Yes, configure output path to docs/api in your repository. Add GitHub Actions workflow running scribe:generate on push, committing results to gh-pages branch. Static HTML requires no build step on Pages. This provides free, automated hosting synchronized with your Laravel application release cycle.

Define global headers in config/scribe.php under routes.global_headers array. Specify key-value pairs like X-API-Version or Accept-Language that apply universally. These persist across all generated examples and Postman collections without requiring repetitive attribute declarations on individual controller methods.