Global middleware on the HTTP client
Requizon registers one middleware on Laravel's HTTP client factory. Http::get(), Http::pool() and the packages that call through them are recorded from the next request, and there is no call to wrap or forget.
You have a dashboard for the requests coming in. Requizon is the one for the requests going out: every third-party API your application depends on, with duration, status, failure classification and the response body of anything that broke, stored in your own database.
$ composer require boring-o11y/requizon
That is the integration. Your HTTP calls stay exactly as they are and are recorded from the next request.
or read the docs first
/requizon. The orange line is a vendor's bad morning.
Every call through Laravel's HTTP client is recorded. Nothing to opt in per call.
A dashboard served by your own app, with its own views and assets.
Detail rows and their hourly rollup, in MySQL, MariaDB, PostgreSQL or SQLite.
There is no agent or third-party account, and the traffic stays on your servers.
Recording cannot fail the call it records. An exception inside the recorder goes to your application's exception handler and is then swallowed, so the worst a Requizon bug can do is lose a row.
When checkout hangs, the first question is whether your code was slow or the payment provider was. Requizon keeps which API was called, on which path, how long it took, whether it failed and what it sent back when it did.
Requizon registers one middleware on Laravel's HTTP client factory. Http::get(), Http::pool() and the packages that call through them are recorded from the next request, and there is no call to wrap or forget.
A host is its own API until you map it. Point api.stripe.com and files.stripe.com at "stripe" and the dashboard lists one API, which opens onto its hosts, paths and individual calls.
The response headers of a failed call are stored by default, request headers when you ask for them, and either direction can be narrowed to the names you list. Authorization, Cookie and anything that looks like an API key keep their name and lose their value.
A connection that never landed, a 4xx or 5xx, and a 200 with an error in the body are stored as different failure types. A connection error also records its cause, such as a timeout or a failed DNS lookup.
Each page opens with the failures of the last quarter hour, by type, with when the last one happened. The API and path tables count them per row and sort whatever is failing now to the top, so a vendor that broke five minutes ago is not hidden inside a three-day average.
A scheduled command folds calls into hourly buckets keyed by API, host and path, and the charts read their window, from 24 hours to a fortnight, off those buckets. The few minutes since its last run are counted straight from the detail rows, so the hour under way is on the chart too.
Paths are stored as /orders/:id, so the rollup does not gain a row for every order. When an identifier does not look like one, a rewrite rule names the path to store, and requizon:merge-paths folds the rows already recorded into it.
Requizon adds one middleware to Laravel's HTTP client. Every request the client builds carries it, so the calls you already make are recorded as they are, including the ones buried in a package you did not write. The API name comes from the host, until you give several hosts one name in config.
// Unmapped hosts name themselves: api.github.com
'apis' => [
'stripe' => ['api.stripe.com', 'files.stripe.com'],
'nausys' => ['ws.nausys.com', 'ws2.nausys.com'],
's3' => ['*.s3.eu-west-1.amazonaws.com'],
],
'ignore_hosts' => [
'*.datadoghq.com',
],
// app/Providers/RequizonServiceProvider.php
Requizon::detectFailuresUsing(function ($body, $response, $request) {
if ($request->getUri()->getHost() !== 'ws.nausys.com') {
return null;
}
return $body['errorMessage'] ?? null;
});
// Recorded as:
// failure_type application_error
// failure_message "PERIOD_NOT_AVAILABLE"
// status_code 200
A depressing number of APIs answer 200 OK
with the real answer buried in the body. Register one callback that knows what failure looks like
for each API, and those calls are classified as
application_error, with the message
the API actually gave you. A 100% success rate then means the calls worked.
Each level of the dashboard charts response time and responses for whatever it lists. Click an API and you get its paths, with the slowest ones drawn as their own lines. Click Failures and the chart narrows to failures by type, so an outage stands out instead of sitting on top of ten thousand successful calls.
A recorder for outbound calls sits where your API keys and passwords travel. When Requizon cannot tell whether something is a secret, it leaves it out.
Stored
Kept out
"api_key": "***",
"_unparsed": "application/xml, 1482 bytes"
Redaction works by parameter name, so a body Requizon cannot parse into named
parameters (XML, SOAP, text/plain) is stored as its content type and size. Both name lists,
and that default, can be changed in config/requizon.php.
A failed response body Requizon cannot parse is stored as it came.
One that parses as JSON or a form goes through the same redaction as the request. XML, plain text
and anything cut off at the size limit do not, so an API that echoes your credentials back in an
error will put them in
requizon_http_requests for as long as
your detail retention allows. Keep that window short and treat the table as sensitive.
What bounds it
A private Composer registry, a migration, a service provider with a gate in it, and the scheduler you are probably running already.
Point Composer at the private registry with your license key.
composer config --global \ --auth http-basic.requizon.composer.sh \ you@example.com YOUR-LICENSE-KEY
Pull in the package and create the three tables it reads and writes. Recording starts here.
composer require boring-o11y/requizon php artisan migrate
Publish the provider, add it to bootstrap/providers.php, then narrow the gate to whoever should see the dashboard.
php artisan vendor:publish \ --tag=requizon-provider
requizon:aggregate rolls calls into hourly buckets every five minutes, and requizon:prune deletes old rows. Without schedule:run in cron, the dashboard slows and the tables never shrink.
* * * * * cd /path-to-app \ && php artisan schedule:run
class RequizonServiceProvider extends RequizonApplicationServiceProvider
{
protected function gate(): void
{
Gate::define('viewRequizon', fn ($user) => $user->hasRole('administrator'));
}
}
Until that gate exists, the dashboard answers only in the
local environment, so a forgotten install does not
expose your outbound traffic in production.
The full installation guide →
The payment includes twelve months of upgrades, and what you receive in that year is yours permanently, whether or not you ever renew.
Secure checkout by Anystack, with cards and VAT invoices.
Requizon is a Laravel package that gives you a dashboard for your application's outbound HTTP traffic. It records every call made through Laravel's HTTP client: duration, status, failure classification, redacted query and body parameters, and the response headers and body of anything that failed. It rolls that up into hourly buckets and serves a self-contained UI at /requizon. It is made by Boring Observability, who also make Skyline for Laravel.
It registers a global middleware on Laravel's HTTP client factory, so every request built through the Http facade carries it, Http::pool() included, with nothing to change at the call site. Recording rides on Guzzle's on_stats hook, which reports every transfer, including one where the connection failed outright and there was no response at all, and the row is written once the call has settled, before your code receives the result. SDKs that build their own Guzzle client are covered by pushing the same middleware onto their handler stack.
Yes. List them in ignore_hosts in config/requizon.php, as Str::is() patterns such as *.datadoghq.com, and matching calls are not recorded at all. Your telemetry backends belong there first: an exporter shipping logs or metrics over HTTP would otherwise record its own traffic.
Recording is a single insert after the transfer completes, and the aggregation that makes the dashboard fast runs out of band on the scheduler. Every failure inside the recorder is reported through your application's exception handler and then swallowed, so an instrumentation problem can never break the call it is observing. Successful response bodies are only read if you register a failure detector, and streamed responses are never read.
No. Requizon is a package running inside your application, writing to three tables in your own database, and serving its own dashboard from your own domain. There is no agent, no SaaS account and no egress. That also makes it usable on traffic you are contractually not allowed to send to a third party.
Three things, recorded distinctly. A connection_error is a transfer that never produced a whole response: DNS, TLS, a timeout, or a connection that dropped part-way through the body. An http_error is any response with a status of 400 or above. An application_error is a successful HTTP response that your own callback classified as a failure, which is how you catch APIs that answer 200 OK with the error in the body.
As little as it can. Parameters whose name contains pass, secret, token, apikey, api_key or auth, and those named exactly p, l or pwd, are stored as ***, in the query string and in the body alike; both lists are configurable, and a provider's own key names can be declared on its entry in the apis map. Sensitive headers keep their name and lose their value, so you can see that an Authorization header was sent without storing the token. Two things can still carry a credential: a request body Requizon cannot parse into named parameters (XML, SOAP, text/plain), which is recorded by shape only unless you opt in, and a failed response body that does not parse as JSON or a form, which is stored as it came. Keep detail retention short and treat the table as sensitive.
Telescope stores each outgoing call in full, and its default filter keeps none of them outside the local environment. Laravel Nightwatch does record outgoing requests in production, grouped by host with status counts and response times, as one part of a hosted APM that sends your data to Laravel and recommends sampling it. Requizon records only outbound calls, all of them, broken down by API, host and path and by failure type, in your own database. It pairs well with either: the APM tells you a request was slow, and Requizon tells you how that vendor has been behaving all week.
It records the response headers of calls that failed, and no request headers, until you say otherwise. Each direction is set to none, failures or all, and can be narrowed to the header names you list, so you can keep Retry-After and X-RateLimit-* on every call without keeping the rest. Headers that look like credentials are kept with their values replaced by ***: Authorization, Cookie, anything containing api-key, and any name matching the parameter redaction rules for that API.
PHP 8.2 or newer, Laravel 12 or 13, and one of MySQL 8.0.19+, MariaDB 10.5+, PostgreSQL 9.5+ or SQLite 3.24+. The hourly rollup upserts its buckets, and those are the versions where each database got an upsert. The tables can live on a separate connection from your application's own.
By default, individual request rows for 14 days and hourly aggregates for a year. Both are pruned daily by a command Requizon schedules for you, and both retention windows are configurable. Re-aggregating a window is idempotent, so overlapping runs are safe.
Requizon is a one-time purchase of $9.99 per application. The purchase includes twelve months of upgrades. Everything published during those twelve months is yours permanently: you can keep running it and reinstall it whenever you rebuild. Renewing is optional and buys only the next year of releases. The checkout is hosted by Anystack, which also issues the license key.
Yes. Every purchase comes with a 30-day money-back guarantee. Email tech@boring-observability.dev within 30 days of being charged and we refund it in full, no questions asked and no call to sit through.
Whoever your viewRequizon gate says can. You publish a service provider, define the gate against your own roles or user check, and Requizon consults it on every dashboard request. Until that gate exists the dashboard is reachable only in the local environment.
Checkout takes a minute and ends with your license key. One composer require later, your outbound calls are on a dashboard.
Secure checkout by Anystack. 30 days to change your mind.
Questions first? Email us and one of the people who builds it will answer.