# Installing Requizon

> Require the package, migrate, register the provider and narrow the gate. Recording starts on the next request.

Source: https://requizon.boring-observability.dev/docs/installation
Section: Getting started — Requizon documentation (version 0.2)
Updated: 2026-09-16

---

Requizon is a Laravel package with its own routes, views and assets, in the same shape as Horizon. Installing it takes a Composer registry, a migration and one service provider. There is nothing to change in the code that makes HTTP calls: from the next request onwards, every call through Laravel's HTTP client is recorded.

## Requirements

- PHP 8.2 or newer
- Laravel 12 or 13
- MySQL 8.0.20 or newer, or a compatible MariaDB. The hourly rollup uses `INSERT ... ON DUPLICATE KEY UPDATE` with row aliases, and `requizon:aggregate` refuses to run on any other driver. Recording itself is a plain insert, so the tables can live on a [separate MySQL connection](https://requizon.boring-observability.dev/docs/configuration#storage) if your application's default database is something else.
- The Laravel scheduler running on at least one server (`php artisan schedule:run` every minute)

## 1. Authenticate Composer

Requizon is distributed through a private Composer registry. Your license key is issued through [Anystack](https://anystack.sh/) at checkout. Run this once on every machine that installs the package, including CI and build servers:

```bash
composer config --global --auth http-basic.requizon.composer.sh \
  you@example.com \
  YOUR-LICENSE-KEY
```

The username is the email address the license belongs to and the key is the password. On servers, supply the same pair through `COMPOSER_AUTH` instead of committing an `auth.json`:

```bash
export COMPOSER_AUTH='{"http-basic":{"requizon.composer.sh":{"username":"you@example.com","password":"YOUR-LICENSE-KEY"}}}'
```

## 2. Require the package and migrate

Add the registry to your application's `composer.json`:

```json
{
    "repositories": [
        { "type": "composer", "url": "https://requizon.composer.sh" }
    ]
}
```

Then require Requizon and run the migrations:

```bash
composer require boring-o11y/requizon
php artisan migrate
```

Package discovery registers Requizon's own service provider, which installs the recording middleware and the dashboard routes. The migrations create three tables:

| Table | Holds |
| --- | --- |
| `http_requests` | One row per transfer: API, host, path, status, duration, failure, redacted parameters, and the response body of a failure. Kept for 14 days by default. |
| `http_request_stats` | The hourly rollup the dashboard reads, keyed by API, host, path and hour. Kept for a year. |
| `http_request_outcome_stats` | The same buckets broken down by status code and failure type, for the responses charts. |

Both migrations skip a table that already exists. An application that was recording outbound calls into `http_requests` before it adopted Requizon keeps its tables, and rolling back leaves them in place, because Requizon only drops tables it created itself.

## 3. Publish the provider and define the gate

```bash
php artisan vendor:publish --tag=requizon-provider
```

This writes `app/Providers/RequizonServiceProvider.php`. Register it in `bootstrap/providers.php`, since publishing a file does not register it:

```php
return [
    App\Providers\AppServiceProvider::class,
    App\Providers\RequizonServiceProvider::class,
];
```

Then narrow the gate to whoever should see your outbound traffic:

```php
namespace App\Providers;

use BoringO11y\Requizon\RequizonApplicationServiceProvider;
use Illuminate\Support\Facades\Gate;

class RequizonServiceProvider extends RequizonApplicationServiceProvider
{
    protected function gate(): void
    {
        Gate::define('viewRequizon', fn ($user) => $user->hasRole('administrator'));
    }
}
```

In the `local` environment the dashboard is open to everyone. Everywhere else it asks the gate, and the published gate returns `false` until you change it. A forgotten install is a dashboard nobody can reach, which is the failure you want.

> **The provider does two jobs**
>
> The same provider's `boot()` method is where Requizon's callbacks go: the [failure detector](https://requizon.boring-observability.dev/docs/failure-detection) for APIs that answer `200 OK` with an error, and the [API](https://requizon.boring-observability.dev/docs/naming-apis) and [path](https://requizon.boring-observability.dev/docs/paths) resolvers. The published stub has the detector commented out, ready to fill in.

## 4. Make sure the scheduler runs

Requizon registers two commands on Laravel's scheduler: `requizon:aggregate` every five minutes, which builds the rollup the dashboard reads, and `requizon:prune` once a day. If `schedule:run` is already in your crontab there is nothing to add. If it is not, calls are recorded but the dashboard stays empty:

```bash
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
```

To run the commands on your own schedule instead, see [Retention and scheduling](https://requizon.boring-observability.dev/docs/retention).

## Checking it works

`php artisan about` gains a Requizon section:

```text
Requizon .............................................................
  Instrumentation ................................... Laravel HTTP client
  Dashboard ............................................... /requizon
```

Then make an outbound call:

```bash
php artisan tinker --execute="Http::get('https://api.github.com/zen')"
```

The overview at `/requizon` and its charts read the hourly rollup, and `requizon:aggregate` only rolls up hours that have ended, so a call made now reaches the overview after the hour turns. The requests list reads the detail table directly, so the call is visible straight away at `/requizon/api.github.com/requests`. Unconfigured hosts name themselves; to group several hosts under one vendor, see [Naming APIs](https://requizon.boring-observability.dev/docs/naming-apis).

## Before you deploy

Recording covers every host your application calls, so read these two before the first production deploy rather than after:

- **Ignore your telemetry backends.** An exporter that ships logs or metrics over HTTP otherwise records its own traffic. See [ignoring hosts](https://requizon.boring-observability.dev/docs/what-gets-recorded#ignoring-hosts).
- **Know what is stored.** Request parameters are redacted by name, but the response body of a failed call is kept as-is. See [Redaction and stored data](https://requizon.boring-observability.dev/docs/redaction).

And in your own test suite, set `REQUIZON_ENABLED=false` in `phpunit.xml` unless you want every `Http::fake()` response written to `http_requests`.


## Common questions

### Do I have to change my HTTP calls to use Requizon?

No. Requizon installs its recording middleware on Laravel's HTTP client factory, so every call made through the Http facade is recorded, Http::pool() included. There is nothing to add at the call site.

### Why does the Requizon dashboard return 403 in production?

Outside the local environment the dashboard asks the viewRequizon gate, and the gate denies everyone until you override it in the published App\Providers\RequizonServiceProvider. Also check that the provider is registered in bootstrap/providers.php: without it no gate is defined and the dashboard stays local-only.

### Why is the Requizon dashboard empty after installing?

The dashboard reads an hourly rollup that requizon:aggregate builds every five minutes through Laravel's scheduler. If schedule:run is not running on the server, calls are recorded but never aggregated, so the overview shows nothing. The aggregate also only rolls up hours that have ended, so a fresh install shows its first calls on the overview after the hour turns. The requests list for an API reads the detail table and shows them immediately.
