Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Ohdear Php Sdk Laravel Package

ohdearapp/ohdear-php-sdk

Official PHP SDK for the Oh Dear API, built on Saloon v4. Manage monitors, retrieve user info, and handle API responses with typed DTOs. Supports API token authentication, configurable timeouts, and clear validation and error exceptions. Simplifies integrating Oh Dear monitoring into PHP application...

View on GitHub
Deep Wiki
Context7

Getting Started

Install and authenticate in five minutes.

composer require ohdearapp/ohdear-php-sdk

Create a token at https://ohdear.app/user/api-tokens, then keep it out of code:

# .env
OH_DEAR_TOKEN=your-api-token
OH_DEAR_TEAM_ID=1
// config/services.php
'oh_dear' => [
    'token' => env('OH_DEAR_TOKEN'),
    'timeout' => env('OH_DEAR_TIMEOUT', 10),
],

The package ships no service provider and no facade — it's a plain PHP SDK built on Saloon v4, so you wire it yourself in AppServiceProvider. Do this once as a singleton; the client is stateless and safe to reuse.

use OhDear\PhpSdk\OhDear;

$this->app->singleton(OhDear::class, fn () => new OhDear(
    token: config('services.oh_dear.token'),
    timeoutInSeconds: config('services.oh_dear.timeout'),
));

Where to look first: vendor/ohdearapp/ohdear-php-sdk/src/OhDear.php is the entire public surface — one method per API endpoint, each returning a DTO or an iterator of DTOs. src/Dto/ and src/Enums/ tell you exactly which properties are typed and which helper methods exist (CheckResult::isUp(), CertificateHealth::getFailedChecks(), …). Read those two directories before the README; they're the accurate contract.

First use case for a Laravel team: a nightly artisan command that surfaces anything red in the ops channel.

// app/Console/Commands/OhDearHealthDigest.php
$ohDear = app(OhDear::class);

foreach ($ohDear->monitors() as $monitor) {
    $summary = $ohDear->checkSummary($monitor->id, CheckType::Uptime);

    if ($summary->checkResult()->isDown()) {
        logger()->warning("Oh Dear: {$monitor->url} is down", [
            'summary' => $summary->summary,
            'result' => $summary->result,
        ]);
    }
}

That's the whole loop: resolve the singleton → iterate monitors → ask for a typed check summary → branch on the enum.


Implementation Patterns

Wrap the client in your own thin service

Don't sprinkle app(OhDear::class) through controllers. Wrap the handful of operations you actually use so you can cache, retry, and fake them in tests.

namespace App\Services;

use OhDear\PhpSdk\OhDear;
use OhDear\PhpSdk\Enums\CheckType;

class OhDearService
{
    public function __construct(private readonly OhDear $ohDear) {}

    /** @return \OhDear\PhpSdk\Dto\Monitor[] */
    public function downMonitors(): array
    {
        return collect($this->ohDear->monitors())
            ->filter(fn ($m) => $this->ohDear
                ->checkSummary($m->id, CheckType::Uptime)
                ->checkResult()
                ->isDown())
            ->values()
            ->all();
    }
}

Now the controller, the command, and the queue job all depend on OhDearService — which is trivially mockable — and only one class knows what Saloon looks like.

Maintenance windows around deploys

This is the highest-value day-to-day use for a Laravel team: stop Oh Dear paging you while php artisan migrate and a queue restart are in flight. Wrap it in a command and call it from your deploy script or a php artisan deploy pipeline.

public function handle(OhDear $ohDear): int
{
    $monitor = $ohDear->monitorByUrl(config('app.url'));

    try {
        $period = $ohDear->startMaintenancePeriod($monitor->id, 1800, 'Deploy ' . config('app.version'));
        $this->info("Maintenance started: {$period->name}");
        // ... run the actual deploy steps elsewhere ...
    } finally {
        $ohDear->stopMaintenancePeriod($monitor->id);
    }
}

stopMaintenancePeriod() kills all active periods on that monitor, so make sure nobody else has one running — or prefer the scheduled variant with explicit starts_at/ends_at for planned windows and startMaintenancePeriod() only for short, self-contained operations.

Incident → status page, automated

Status page updates are a natural fit for an event listener on your own incident model. Severity, title, and text are all plain strings on the payload; pinned keeps the post at the top during long incidents.

// app/Listeners/PostIncidentToStatusPage.php
public function handle(IncidentOpened $event): void
{
    app(OhDear::class)->createStatusPageUpdate([
        'status_page_id' => config('services.oh_dear.status_page_id'),
        'title' => $event->incident->title,
        'text' => $event->incident->public_summary,
        'severity' => 'high',
        'time' => now()->format('Y-m-d H:i:s'),
        'pinned' => true,
    ]);
}

Keep the update ID on the incident row so updateStatusPageUpdate($id, ['title' => 'Resolved']) on close is a one-liner, and deleteStatusPageUpdate() is available if you post something in error.

Certificate expiry as a dashboard widget

certificateHealth() returns far more than a boolean: issuer, validity dates, per-check pass/fail, and the chain. Cache it — certificates change on the order of weeks, not seconds.

$health = Cache::remember("ohdear.cert.{$monitorId}", now()->addHours(6),
    fn () => app(OhDear::class)->certificateHealth($monitorId));

return [
    'issuer' => $health->getIssuer(),
    'valid_until' => $health->getValidUntil(),
    'healthy' => $health->isHealthy(),
    'failed' => $health->getFailedChecks(),   // ['label' => ..., 'type' => ...]
];

getFailedChecks() is the useful bit — it gives you a human label per failing validation (domain_matches, not_expired, …) that you can render directly, instead of a bare "unhealthy" flag.

Snooze / enable / disable as an admin action

Check-level controls are per-$checkId, and the IDs live on the monitor's checks collection — resolve them from the monitor, don't hardcode.

$monitor = $ohDear->monitor($monitorId);
$check = collect($monitor->checks)->firstWhere('type', 'broken_links');

$ohDear->snoozeCheck($check->id, 120);   // minutes, 1–144000
// later
$ohDear->unsnoozeCheck($check->id);

requestCheckRun($checkId, $headers) is the one to reach for after a deploy when you want proof the site is back before closing the maintenance window — for uptime checks you can pass custom headers so the probe matches a real browser.

Metrics for internal reporting

Uptime metrics are date-ranged and pre-split — pull them into a queued job and persist to your own table rather than querying on page load. Use UptimeMetricsSplit::Hour for a 24h view and a coarser split for weekly reports, and always format dates as Y-m-d H:i:s.


Gotchas and Tips

isUp() is true for Warning. This is the single most common surprise. CheckResult::Warning means the primary location reports down but the secondary confirms the site is reachable — a partial connectivity issue, not an outage. If you alert on ! isUp() you'll page people for warnings; if you alert on isDown() you won't. Decide deliberately and, if you want warning-level alerts, gate on isWarning() separately.

Everything throws — plan for it. API errors throw OhDearException; 422s throw ValidationException with a readable getMessage() and a structured errors() array. In queued jobs this means a bad payload retries pointlessly:

try {
    $ohDear->createMonitor($payload);
} catch (ValidationException $e) {
    throw new \App\Exceptions\PermanentJobFailure($e->getMessage(), errors: $e->errors());
}

Catch ValidationException before OhDearException — it extends it in most Saloon-based SDKs, and a blanket catch will swallow your validation detail.

There is no built-in retry or 429 backoff. Errors are surfaced, not retried. If you're calling this from a loop over hundreds of monitors, you will hit rate limits. Two mitigations: (a) cache aggressively — monitors, certificates, and status pages rarely change minute-to-minute; (b) send through a Saloon sender configured with retry/backoff, or add ->middleware() on the connector if the SDK exposes it. Check OhDear.php for a connector accessor before assuming.

Iterators make multiple HTTP requests. $ohDear->monitors(), statusPages(), maintenancePeriods() return lazy iterators backed by paginated endpoints. ->count() or iterator_to_array() forces the full fetch. Inside a foreach with a checkSummary() call per monitor, you're making N+1 requests — batch what you can, cache what you can't, and never put that loop in a web request that renders a page.

Named arguments in the constructor. new OhDear('token', timeoutInSeconds: 30) — the timeout is the second parameter, but pass it by name. The default 10 seconds is tight for a slow Oh Dear response during an incident; bump it for read-heavy commands, and lower it (3–5s) for anything on a user-facing request path.

Dates are plain strings, server-side interpreted. httpUptimeMetrics($id, $start, $end, $split), createMaintenancePeriod(['starts_at' => ...]), and status page update time all take Y-m-d H:i:s strings. Format explicitly with ->format('Y-m-d H:i:s') rather than leaning on __toString() or ISO-8601, and think about whose timezone Oh Dear assumes — it's not config('app.timezone').

Snooze is bounded to 1–144000 minutes (~100 days). Passing 0 or a negative throws. Prefer a snooze over disabling a check: a snooze auto-expires, a disabled check stays disabled forever and you'll forget it exists.

Don't Http::fake() it. This SDK bypasses Laravel's HTTP client entirely, so Http::fake() and Http::assertSent() do nothing. Tests use Saloon's own MockClient/MockResponse injected into the client, or — much simpler — mock your OhDearService wrapper and assert on your own domain method calls. If you do need wire-level tests, point the SDK at a recorded fixture and assert the DTO mapping, which is where the real value is.

Token handling for multi-tenant apps. If each tenant has its own Oh Dear team, don't store a token in config. Persist it encrypted on the tenant model and build a per-tenant instance rather than the config-driven singleton:

$ohDear = new OhDear($tenant->oh_dear_token, timeoutInSeconds: 15);

In long-lived queue workers (horizon, queue:work without restarts), avoid caching that instance across jobs — tokens get rotated, and a stale client keeps failing silently until the worker recycles. Construct per job.

delete* methods return nothing useful. deleteMonitor(), deleteStatusPage(), deleteMaintenancePeriod() are fire-and-forget; treat a thrown exception as the only failure signal and don't try to inspect a response body.

Extension points. Every call is a discrete Saloon request class (OhDear\PhpSdk\Requests\Monitors\CreateMonitorRequest, GetMonitorsRequest, …). When Oh Dear ships an endpoint the SDK hasn't wrapped yet, you don't have to fork: instantiate the connector the SDK uses and send your own Saloon Request subclass with the same base URL and Authorization: Bearer header. Keep that custom request in your app, not in vendor, and swap it for the SDK method once it lands — the SDK is actively released (last release 2026-03-30), so gaps close quickly.

Pin the major version. It's pre-1.0-ish territory with a small dependent count and recent enum/DTO additions (the CheckResult helpers are newer than the raw result string). composer require "ohdearapp/ohdear-php-sdk:^1.0" and commit the lockfile; read the CHANGELOG on minor bumps, because enum cases and DTO property names have moved before.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
adachsoft/php-code-style