# Openstatus PHP SDK [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![PHP 8.4+](https://img.shields.io/badge/PHP-8.4%2B-777BB4.svg)](https://www.php.net/) Official PHP SDK for [Openstatus](https://openstatus.dev) — the open-source status page with uptime monitoring. ## Table of Contents - [Features](#features) - [Installation](#installation) - [Quick Start](#quick-start) - [Authentication](#authentication) - [SDK Reference](#sdk-reference) - [Monitor Service](#monitor-service) - [Health Service](#health-service) - [Status Report Service](#status-report-service) - [Status Page Service](#status-page-service) - [Maintenance Service](#maintenance-service) - [Notification Service](#notification-service) - [Reference](#reference) - [Monitor Options](#monitor-options) - [Assertions](#assertions) - [Regions](#regions) - [Enums](#enums) - [Working with int64 fields](#working-with-int64-fields) - [Error Handling](#error-handling) - [Framework integration](#framework-integration) - [Related](#related) ## Features ### Monitoring - **HTTP Monitoring** — monitor websites and APIs with customizable assertions. - **TCP Monitoring** — check database connections and other TCP services. - **DNS Monitoring** — verify DNS records and resolution. - **Global Regions** — monitor from 28 locations worldwide. ### Status Page - **Status Pages** — create and manage public status pages with custom domains. - **Page Components** — add monitor-based or static components with grouping. - **Subscribers** — manage email subscriptions for status updates. - **Status Reports** — manage incident reports with update timelines. - **Maintenance Windows** — schedule and manage planned maintenance periods. ### Notifications - **12 providers** — Slack, Discord, Email, PagerDuty, Opsgenie, Telegram, and more. - **Webhook Support** — custom webhooks with headers for any integration. - **Monitor Alerts** — get notified when monitors go down or recover. ### Developer Experience - **Type-safe** — strongly-typed request/response messages generated from Openstatus protobuf schemas (`buf.build/openstatus/api`). - **PSR-18 transport** — bring your own HTTP client, or use the bundled Guzzle implementation. - **No proto toolchain needed** — pre-generated PHP classes ship with the package; `composer require` is all you need. ## Installation Requires PHP 8.4 or later. ```bash composer require openstatus/sdk-php ``` ## Quick Start ```php monitor->v1->monitorService->createHTTPMonitor( new CreateHTTPMonitorRequest([ 'monitor' => new HTTPMonitor([ 'name' => 'My API', 'url' => 'https://api.example.com/health', 'periodicity' => Periodicity::PERIODICITY_1M, 'method' => HTTPMethod::HTTP_METHOD_GET, 'regions' => [ Region::REGION_FLY_AMS, Region::REGION_FLY_IAD, Region::REGION_FLY_SYD, ], 'active' => true, 'status_code_assertions' => [ new StatusCodeAssertion([ 'comparator' => NumberComparator::NUMBER_COMPARATOR_EQUAL, 'target' => 200, ]), ], ]), ]), ); $monitor = $response->getMonitor(); printf("Monitor created: %s\n", $monitor?->getId() ?? 'unknown'); // List all monitors $list = $client->monitor->v1->monitorService->listMonitors(new ListMonitorsRequest()); printf("Found %d monitors\n", $list->getTotalSize()); ``` ## Authentication All API requests require an API key. Get yours from the [Openstatus dashboard](https://www.openstatus.dev/app). ### Recommended: configure the client once ```php use Openstatus\Sdk\ClientOptions; use Openstatus\Sdk\OpenstatusClient; $client = new OpenstatusClient(new ClientOptions( apiKey: getenv('OPENSTATUS_API_KEY') ?: null, )); // No need to pass headers on each call. $client->monitor->v1->monitorService->listMonitors(new ListMonitorsRequest()); ``` ### Alternative: per-call headers Every service method accepts an optional second argument: an array of headers merged into the request. Useful for tracing IDs or one-off overrides. ```php $client->monitor->v1->monitorService->listMonitors( new ListMonitorsRequest(), ['x-trace-id' => 'trace-abc'], ); ``` ### Environment Variables | Variable | Description | Default | |----------------------|-------------------------|----------------------------------| | `OPENSTATUS_API_KEY` | Your Openstatus API key | Required for authenticated calls | | `OPENSTATUS_API_URL` | Custom API endpoint | `https://api.openstatus.dev` | If `apiKey` is not passed in `ClientOptions`, the SDK falls back to `OPENSTATUS_API_KEY`. Same for `baseUrl` / `OPENSTATUS_API_URL`. ## SDK Reference > All examples below assume: > > ```php > $client = new OpenstatusClient(new ClientOptions( > apiKey: getenv('OPENSTATUS_API_KEY') ?: null, > )); > $service = $client->monitor->v1->monitorService; > ``` ### Monitor Service Manage HTTP, TCP, and DNS monitors. #### `createHTTPMonitor(request)` Create an HTTP/HTTPS monitor. ```php use Openstatus\Monitor\V1\CreateHTTPMonitorRequest; use Openstatus\Monitor\V1\HTTPMethod; use Openstatus\Monitor\V1\HTTPMonitor; use Openstatus\Monitor\V1\Periodicity; use Openstatus\Monitor\V1\Region; $response = $service->createHTTPMonitor(new CreateHTTPMonitorRequest([ 'monitor' => new HTTPMonitor([ 'name' => 'My Website', 'url' => 'https://example.com', 'periodicity' => Periodicity::PERIODICITY_1M, 'method' => HTTPMethod::HTTP_METHOD_GET, 'regions' => [Region::REGION_FLY_AMS, Region::REGION_FLY_IAD], 'active' => true, ]), ])); ``` #### `updateHTTPMonitor(request)` Update an existing HTTP monitor. ```php use Openstatus\Monitor\V1\UpdateHTTPMonitorRequest; $service->updateHTTPMonitor(new UpdateHTTPMonitorRequest([ 'id' => 'mon_123', 'monitor' => new HTTPMonitor([ 'name' => 'Renamed monitor', 'url' => 'https://example.com', 'periodicity' => Periodicity::PERIODICITY_5M, 'active' => true, ]), ])); ``` #### `createTCPMonitor(request)` / `updateTCPMonitor(request)` Monitor TCP services. ```php use Openstatus\Monitor\V1\CreateTCPMonitorRequest; use Openstatus\Monitor\V1\TCPMonitor; $service->createTCPMonitor(new CreateTCPMonitorRequest([ 'monitor' => new TCPMonitor([ 'name' => 'Postgres prod', 'url' => 'db.example.com:5432', 'periodicity' => Periodicity::PERIODICITY_1M, 'regions' => [Region::REGION_FLY_AMS], 'active' => true, ]), ])); ``` #### `createDNSMonitor(request)` / `updateDNSMonitor(request)` Monitor DNS records. ```php use Openstatus\Monitor\V1\CreateDNSMonitorRequest; use Openstatus\Monitor\V1\DNSMonitor; use Openstatus\Monitor\V1\DNSRecordType; $service->createDNSMonitor(new CreateDNSMonitorRequest([ 'monitor' => new DNSMonitor([ 'name' => 'apex A record', 'url' => 'example.com', 'record_type' => DNSRecordType::DNS_RECORD_TYPE_A, 'periodicity' => Periodicity::PERIODICITY_5M, 'regions' => [Region::REGION_FLY_IAD], 'active' => true, ]), ])); ``` #### `getMonitor(request)` Fetch a single monitor (returns a `MonitorConfig` discriminated by type). ```php use Openstatus\Monitor\V1\GetMonitorRequest; $response = $service->getMonitor(new GetMonitorRequest(['id' => 'mon_123'])); $config = $response->getMonitor(); if ($config?->getHttp() !== null) { echo $config->getHttp()->getUrl(); } ``` #### `listMonitors(request)` List all monitors in the workspace, grouped by type. ```php use Openstatus\Monitor\V1\ListMonitorsRequest; $response = $service->listMonitors(new ListMonitorsRequest(['limit' => 100, 'offset' => 0])); foreach ($response->getHttpMonitors() as $monitor) { echo $monitor->getName() . "\n"; } ``` #### `triggerMonitor(request)` Manually run a monitor immediately. Rate-limited by the synthetic-checks quota. ```php use Openstatus\Monitor\V1\TriggerMonitorRequest; $service->triggerMonitor(new TriggerMonitorRequest(['id' => 'mon_123'])); ``` #### `deleteMonitor(request)` ```php use Openstatus\Monitor\V1\DeleteMonitorRequest; $service->deleteMonitor(new DeleteMonitorRequest(['id' => 'mon_123'])); ``` #### `getMonitorStatus(request)` Per-region status snapshot. ```php use Openstatus\Monitor\V1\GetMonitorStatusRequest; use Openstatus\Monitor\V1\MonitorStatus; use Openstatus\Monitor\V1\Region; $response = $service->getMonitorStatus(new GetMonitorStatusRequest(['id' => 'mon_123'])); foreach ($response->getRegions() as $region) { printf( "%s: %s\n", Region::name($region->getRegion()), MonitorStatus::name($region->getStatus()), ); } ``` #### `getMonitorSummary(request)` Aggregated latency percentiles and request counts. ```php use Openstatus\Monitor\V1\GetMonitorSummaryRequest; use Openstatus\Monitor\V1\TimeRange; $summary = $service->getMonitorSummary(new GetMonitorSummaryRequest([ 'id' => 'mon_123', 'time_range' => TimeRange::TIME_RANGE_7D, ])); printf("p95: %dms\n", (int) $summary->getP95()); ``` #### `listMonitorHTTPResponseLogs(request)` Recent HTTP response log entries with pagination. ```php use Openstatus\Monitor\V1\ListMonitorHTTPResponseLogsRequest; $response = $service->listMonitorHTTPResponseLogs(new ListMonitorHTTPResponseLogsRequest([ 'id' => 'mon_123', 'limit' => 50, ])); $pagination = $response->getPagination(); foreach ($response->getLogs() as $log) { printf("%dms %s\n", $log->getLatency(), $log->hasStatusCode() ? (string) $log->getStatusCode() : '—'); } if ($pagination?->getHasMore()) { // pass $pagination->getNextOffset() into the next request } ``` #### `getMonitorHTTPResponseLog(request)` Single log entry with full headers and body assertions. ```php use Openstatus\Monitor\V1\GetMonitorHTTPResponseLogRequest; $response = $service->getMonitorHTTPResponseLog(new GetMonitorHTTPResponseLogRequest([ 'id' => 'mon_123', 'log_id' => 'log_abc', ])); $detail = $response->getLog(); if ($detail !== null) { foreach ($detail->getHeaders() as $key => $value) { printf("%s: %s\n", $key, $value); } } ``` ### Health Service #### `check(request)` Returns the API's serving status. No authentication required — useful as a liveness check. ```php use Openstatus\Health\V1\CheckRequest; use Openstatus\Health\V1\CheckResponse\ServingStatus; $response = $client->health->v1->healthService->check(new CheckRequest()); echo ServingStatus::name($response->getStatus()); ``` ### Status Report Service Manage incident reports with a lifecycle: `investigating → identified → monitoring → resolved`. #### `createStatusReport(request)` ```php use Openstatus\Status_report\V1\CreateStatusReportRequest; use Openstatus\Status_report\V1\StatusReport; use Openstatus\Status_report\V1\StatusReportStatus; $service = $client->statusReport->v1->statusReportService; $response = $service->createStatusReport(new CreateStatusReportRequest([ 'report' => new StatusReport([ 'title' => 'Elevated error rate', 'status' => StatusReportStatus::STATUS_REPORT_STATUS_INVESTIGATING, 'status_page_ids' => ['page_123'], ]), ])); ``` #### `getStatusReport(request)` / `listStatusReports(request)` / `updateStatusReport(request)` / `deleteStatusReport(request)` ```php use Openstatus\Status_report\V1\GetStatusReportRequest; use Openstatus\Status_report\V1\ListStatusReportsRequest; use Openstatus\Status_report\V1\UpdateStatusReportRequest; use Openstatus\Status_report\V1\DeleteStatusReportRequest; $service->getStatusReport(new GetStatusReportRequest(['id' => 'rep_123'])); $service->listStatusReports(new ListStatusReportsRequest()); $service->updateStatusReport(new UpdateStatusReportRequest(['id' => 'rep_123', 'report' => $updated])); $service->deleteStatusReport(new DeleteStatusReportRequest(['id' => 'rep_123'])); ``` #### `addStatusReportUpdate(request)` ```php use Openstatus\Status_report\V1\AddStatusReportUpdateRequest; use Openstatus\Status_report\V1\StatusReportUpdate; $service->addStatusReportUpdate(new AddStatusReportUpdateRequest([ 'report_id' => 'rep_123', 'update' => new StatusReportUpdate([ 'status' => StatusReportStatus::STATUS_REPORT_STATUS_IDENTIFIED, 'message' => 'Root cause identified, fix deploying.', ]), ])); ``` ### Status Page Service Manage public status pages with components, component groups, and subscribers. #### `createStatusPage(request)` ```php use Openstatus\Status_page\V1\CreateStatusPageRequest; use Openstatus\Status_page\V1\PageTheme; use Openstatus\Status_page\V1\StatusPage; $service = $client->statusPage->v1->statusPageService; $service->createStatusPage(new CreateStatusPageRequest([ 'page' => new StatusPage([ 'title' => 'Acme Status', 'slug' => 'acme', 'theme' => PageTheme::PAGE_THEME_LIGHT, ]), ])); ``` #### `getStatusPage(request)` / `listStatusPages(request)` / `updateStatusPage(request)` / `deleteStatusPage(request)` ```php $service->getStatusPage(new GetStatusPageRequest(['id' => 'page_123'])); $service->listStatusPages(new ListStatusPagesRequest()); $service->updateStatusPage(new UpdateStatusPageRequest(['id' => 'page_123', 'page' => $updatedPage])); $service->deleteStatusPage(new DeleteStatusPageRequest(['id' => 'page_123'])); ``` #### `getStatusPageContent(request)` / `getOverallStatus(request)` ```php $content = $service->getStatusPageContent(new GetStatusPageContentRequest(['slug' => 'acme'])); $status = $service->getOverallStatus(new GetOverallStatusRequest(['id' => 'page_123'])); ``` #### Components Add a monitor-backed component or a static component, update it, remove it. ```php use Openstatus\Status_page\V1\AddMonitorComponentRequest; use Openstatus\Status_page\V1\AddStaticComponentRequest; use Openstatus\Status_page\V1\RemoveComponentRequest; use Openstatus\Status_page\V1\UpdateComponentRequest; $service->addMonitorComponent(new AddMonitorComponentRequest([ 'page_id' => 'page_123', 'monitor_id' => 'mon_456', 'name' => 'API', ])); $service->addStaticComponent(new AddStaticComponentRequest([ 'page_id' => 'page_123', 'name' => 'Marketing site', ])); $service->updateComponent(new UpdateComponentRequest(['id' => 'comp_1', 'name' => 'Renamed'])); $service->removeComponent(new RemoveComponentRequest(['id' => 'comp_1'])); ``` #### Component groups ```php use Openstatus\Status_page\V1\CreateComponentGroupRequest; use Openstatus\Status_page\V1\DeleteComponentGroupRequest; use Openstatus\Status_page\V1\UpdateComponentGroupRequest; $service->createComponentGroup(new CreateComponentGroupRequest(['page_id' => 'page_123', 'name' => 'Edge'])); $service->updateComponentGroup(new UpdateComponentGroupRequest(['id' => 'grp_1', 'name' => 'Edge servers'])); $service->deleteComponentGroup(new DeleteComponentGroupRequest(['id' => 'grp_1'])); ``` #### Subscribers ```php use Openstatus\Status_page\V1\CreatePageSubscriptionRequest; use Openstatus\Status_page\V1\ListSubscribersRequest; use Openstatus\Status_page\V1\SubscribeToPageRequest; use Openstatus\Status_page\V1\UnsubscribeFromPageRequest; $service->subscribeToPage(new SubscribeToPageRequest(['page_id' => 'page_123', 'email' => 'user@example.com'])); $service->createPageSubscription(new CreatePageSubscriptionRequest(['page_id' => 'page_123', 'email' => 'admin@example.com'])); $service->listSubscribers(new ListSubscribersRequest(['page_id' => 'page_123'])); $service->unsubscribeFromPage(new UnsubscribeFromPageRequest(['token' => 'unsub_token'])); ``` ### Maintenance Service Schedule planned maintenance windows. Subscribers can be notified automatically. ```php use Openstatus\Maintenance\V1\CreateMaintenanceRequest; use Openstatus\Maintenance\V1\DeleteMaintenanceRequest; use Openstatus\Maintenance\V1\GetMaintenanceRequest; use Openstatus\Maintenance\V1\ListMaintenancesRequest; use Openstatus\Maintenance\V1\Maintenance; use Openstatus\Maintenance\V1\UpdateMaintenanceRequest; $service = $client->maintenance->v1->maintenanceService; $service->createMaintenance(new CreateMaintenanceRequest([ 'maintenance' => new Maintenance([ 'title' => 'Database migration', 'page_id' => 'page_123', 'from_timestamp' => 1730000000000, 'to_timestamp' => 1730003600000, ]), ])); $service->getMaintenance(new GetMaintenanceRequest(['id' => 'mnt_1'])); $service->listMaintenances(new ListMaintenancesRequest(['page_id' => 'page_123'])); $service->updateMaintenance(new UpdateMaintenanceRequest(['id' => 'mnt_1', 'maintenance' => $updated])); $service->deleteMaintenance(new DeleteMaintenanceRequest(['id' => 'mnt_1'])); ``` ### Notification Service Configure notification channels and check usage limits. ```php use Openstatus\Notification\V1\CreateNotificationRequest; use Openstatus\Notification\V1\Notification; use Openstatus\Notification\V1\NotificationProvider; use Openstatus\Notification\V1\NotificationData; use Openstatus\Notification\V1\SlackData; $service = $client->notification->v1->notificationService; $service->createNotification(new CreateNotificationRequest([ 'notification' => new Notification([ 'name' => 'Eng channel', 'provider' => NotificationProvider::NOTIFICATION_PROVIDER_SLACK, 'data' => new NotificationData([ 'slack' => new SlackData(['webhook_url' => 'https://hooks.slack.com/...']), ]), ]), ])); ``` Other methods: ```php use Openstatus\Notification\V1\CheckNotificationLimitRequest; use Openstatus\Notification\V1\DeleteNotificationRequest; use Openstatus\Notification\V1\GetNotificationRequest; use Openstatus\Notification\V1\ListNotificationsRequest; use Openstatus\Notification\V1\SendTestNotificationRequest; use Openstatus\Notification\V1\UpdateNotificationRequest; $service->getNotification(new GetNotificationRequest(['id' => 'notif_1'])); $service->listNotifications(new ListNotificationsRequest()); $service->updateNotification(new UpdateNotificationRequest(['id' => 'notif_1', 'notification' => $updated])); $service->deleteNotification(new DeleteNotificationRequest(['id' => 'notif_1'])); $service->sendTestNotification(new SendTestNotificationRequest(['id' => 'notif_1'])); $service->checkNotificationLimit(new CheckNotificationLimitRequest()); ``` ## Reference ### Monitor Options | Field | Type | Notes | |-------------------------|----------------------------|------------------------------------------------------------------------| | `name` | `string` | Required, max 256 chars. | | `url` | `string` | Required, max 2048 chars. | | `periodicity` | `Periodicity` (enum int) | Required. See [Enums](#enums). | | `method` | `HTTPMethod` (enum int) | HTTP monitors only. Defaults to `HTTP_METHOD_GET`. | | `regions` | `Region[]` (enum int list) | Required. See [Regions](#regions). | | `active` | `bool` | Defaults to false. | | `timeout` | `int` (ms) | 0–120000. Defaults to 45000. | | `degraded_at` | `int` (ms) | Optional latency threshold for "degraded" status. | | `retry` | `int` | 0–10. Defaults to 3. | | `follow_redirects` | `bool` | Defaults to true. | | `body` | `string` | Request body (HTTP). | | `headers` | `Headers[]` | Custom request headers (HTTP). | | `status_code_assertions`| `StatusCodeAssertion[]` | See [Assertions](#assertions). | | `body_assertions` | `BodyAssertion[]` | See [Assertions](#assertions). | | `header_assertions` | `HeaderAssertion[]` | See [Assertions](#assertions). | | `description` | `string` | Optional description. | | `public` | `bool` | Whether the monitor is publicly visible. | ### Assertions Assertions are evaluated against each check response. All matching assertions must pass for a check to be successful. ```php use Openstatus\Monitor\V1\BodyAssertion; use Openstatus\Monitor\V1\HeaderAssertion; use Openstatus\Monitor\V1\NumberComparator; use Openstatus\Monitor\V1\StatusCodeAssertion; use Openstatus\Monitor\V1\StringComparator; $statusCode = new StatusCodeAssertion([ 'comparator' => NumberComparator::NUMBER_COMPARATOR_EQUAL, 'target' => 200, ]); $body = new BodyAssertion([ 'comparator' => StringComparator::STRING_COMPARATOR_CONTAINS, 'target' => 'ok', ]); $header = new HeaderAssertion([ 'key' => 'content-type', 'comparator' => StringComparator::STRING_COMPARATOR_EQUAL, 'target' => 'application/json', ]); ``` ### Regions 28 regions are available via the `Region` enum: ```text REGION_FLY_AMS Amsterdam REGION_FLY_FRA Frankfurt REGION_FLY_ARN Stockholm REGION_FLY_GDL Guadalajara REGION_FLY_ATL Atlanta REGION_FLY_GIG Rio de Janeiro REGION_FLY_BOG Bogotá REGION_FLY_GRU São Paulo REGION_FLY_BOM Mumbai REGION_FLY_HKG Hong Kong REGION_FLY_BOS Boston REGION_FLY_IAD Ashburn REGION_FLY_CDG Paris REGION_FLY_JNB Johannesburg REGION_FLY_DEN Denver REGION_FLY_LAX Los Angeles REGION_FLY_DFW Dallas REGION_FLY_LHR London REGION_FLY_EWR New Jersey REGION_FLY_MAA Chennai REGION_FLY_MAD Madrid REGION_FLY_MIA Miami REGION_FLY_NRT Tokyo REGION_FLY_ORD Chicago REGION_FLY_OTP Bucharest REGION_FLY_PHX Phoenix REGION_FLY_QRO Querétaro REGION_FLY_SCL Santiago REGION_FLY_SEA Seattle REGION_FLY_SIN Singapore REGION_FLY_SJC San Jose REGION_FLY_SYD Sydney REGION_FLY_WAW Warsaw REGION_FLY_YUL Montréal REGION_FLY_YYZ Toronto ``` ### Enums All enums emitted by the protobuf compiler are plain classes with `const int` values. The generated value getters return `int`; use the static `name()` helper to convert to a string. ```php use Openstatus\Monitor\V1\HTTPMethod; use Openstatus\Monitor\V1\Periodicity; HTTPMethod::HTTP_METHOD_GET; // int 1 HTTPMethod::name(HTTPMethod::HTTP_METHOD_GET); // "HTTP_METHOD_GET" Periodicity::PERIODICITY_1M; // int 2 ``` Notable enums you'll encounter: - `Openstatus\Monitor\V1\HTTPMethod` - `Openstatus\Monitor\V1\Periodicity` - `Openstatus\Monitor\V1\Region` - `Openstatus\Monitor\V1\MonitorStatus` - `Openstatus\Monitor\V1\NumberComparator` - `Openstatus\Monitor\V1\StringComparator` - `Openstatus\Monitor\V1\TimeRange` - `Openstatus\Monitor\V1\HTTPResponseLogRequestStatus` - `Openstatus\Monitor\V1\HTTPResponseLogTrigger` - `Openstatus\Health\V1\CheckResponse\ServingStatus` - `Openstatus\Status_report\V1\StatusReportStatus` - `Openstatus\Status_page\V1\OverallStatus` - `Openstatus\Status_page\V1\PageAccessType` - `Openstatus\Status_page\V1\PageTheme` - `Openstatus\Status_page\V1\PageComponentType` - `Openstatus\Notification\V1\NotificationProvider` - `Openstatus\Notification\V1\OpsgenieRegion` ### Working with int64 fields `int64` fields (timestamps, percentiles, latencies measured in nanoseconds) return `int|string` from getters because `protoc-gen-php` falls back to strings on 32-bit platforms to avoid silent overflow. On 64-bit PHP — the default on macOS and Linux — the value is always an `int`. If you need to be safe across both platforms: ```php $timestamp = (int) $log->getTimestamp(); // ms since epoch $p95Ms = (int) $summary->getP95(); ``` The setter accepts either, so passing an `int` directly works. ## Error Handling All Connect RPC errors raise `Openstatus\Sdk\Exception\OpenstatusException` (or a subclass). The exception carries the Connect error code, HTTP status, parsed `details` array, and raw response body. ```php use Openstatus\Sdk\Exception\AuthenticationException; use Openstatus\Sdk\Exception\InvalidArgumentException; use Openstatus\Sdk\Exception\NotFoundException; use Openstatus\Sdk\Exception\OpenstatusException; use Openstatus\Sdk\Exception\RateLimitException; use Openstatus\Sdk\Exception\ServiceUnavailableException; try { $client->monitor->v1->monitorService->createHTTPMonitor($req); } catch (AuthenticationException $e) { // 401 / connectCode "unauthenticated" } catch (NotFoundException $e) { // 404 / "not_found" } catch (InvalidArgumentException $e) { // 400 / "invalid_argument" — inspect $e->details() for field violations foreach ($e->details() as $detail) { printf("%s: %s\n", $detail->type, $detail->value); } } catch (RateLimitException $e) { // 429 / "resource_exhausted" } catch (ServiceUnavailableException $e) { // 5xx, network errors, or non-Connect responses } catch (OpenstatusException $e) { // anything else printf("code=%s http=%d body=%s\n", $e->connectCode(), $e->httpStatus(), $e->rawBody()); } ``` | Connect code | Exception | |------------------------|------------------------------------------------------------------| | `unauthenticated` | `Openstatus\Sdk\Exception\AuthenticationException` | | `not_found` | `Openstatus\Sdk\Exception\NotFoundException` | | `invalid_argument` | `Openstatus\Sdk\Exception\InvalidArgumentException` | | `permission_denied` | `Openstatus\Sdk\Exception\PermissionDeniedException` | | `resource_exhausted` | `Openstatus\Sdk\Exception\RateLimitException` | | `unavailable` | `Openstatus\Sdk\Exception\ServiceUnavailableException` | | anything else | `Openstatus\Sdk\Exception\OpenstatusException` | ## Framework integration ### Symfony Register the client as a service in `config/services.yaml`: ```yaml services: Openstatus\Sdk\ClientOptions: arguments: $apiKey: '%env(OPENSTATUS_API_KEY)%' Openstatus\Sdk\OpenstatusClient: arguments: $options: '@Openstatus\Sdk\ClientOptions' ``` Inject into any controller or service: ```php public function __construct(private OpenstatusClient $openstatus) {} ``` ### Laravel In a service provider's `register()` method: ```php $this->app->singleton(\Openstatus\Sdk\OpenstatusClient::class, function (): \Openstatus\Sdk\OpenstatusClient { return new \Openstatus\Sdk\OpenstatusClient(new \Openstatus\Sdk\ClientOptions( apiKey: env('OPENSTATUS_API_KEY'), )); }); ``` Resolve from the container: ```php $client = app(\Openstatus\Sdk\OpenstatusClient::class); ``` ### Custom PSR-18 client Bring your own HTTP client (Symfony HttpClient, Saloon, Mock, etc.). Anything implementing `Psr\Http\Client\ClientInterface` works. ```php $client = new OpenstatusClient(new ClientOptions( apiKey: getenv('OPENSTATUS_API_KEY') ?: null, httpClient: $myPsr18Client, )); ``` Pair with a custom PSR-3 logger to log every request/response at DEBUG: ```php $client = new OpenstatusClient(new ClientOptions( apiKey: getenv('OPENSTATUS_API_KEY') ?: null, logger: $monolog, )); ``` ## Related - [Openstatus](https://openstatus.dev) — open-source status pages and uptime monitoring. - [Openstatus API docs](https://docs.openstatus.dev) - [Openstatus Node SDK](https://github.com/openstatusHQ/sdk-node) - [Openstatus Proto schema](https://buf.build/openstatus/api) ## License MIT — see [LICENSE](LICENSE).