Laravel SDK

A Composer package with an auto-discovered service provider and ExitProbe facade over Laravel's own HTTP client. This page is the full usage reference: every method, every parameter shape, one working example each. Every method returns the API's decoded JSON body as an associative array.

Install

composer require exitprobe/laravel

The service provider and ExitProbe facade are auto-discovered. Publish the config if you want to tweak defaults:

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

Then set your API token (Settings → API Tokens in your dashboard):

EXITPROBE_API_KEY=your-token

Configuration

// config/exitprobe.php
return [
    'api_key' => env('EXITPROBE_API_KEY'),
    'base_url' => env('EXITPROBE_BASE_URL', 'https://api.exitprobe.com/api/v1'),
    'timeout' => env('EXITPROBE_TIMEOUT', 30),
];

Or resolve ExitProbe\Laravel\ExitProbeClient from the container / inject it, if you’d rather not use the facade.

Error handling

Every non-2xx response throws ExitProbe\Laravel\ExitProbeException, carrying the HTTP status and, for a 422, the field-level validation errors.

use ExitProbe\Laravel\ExitProbeException;

try {
    ExitProbe::monitors()->create(['name' => '']);
} catch (ExitProbeException $e) {
    report($e);
    // $e->status, $e->errors, $e->getMessage()
}

Projects

Every method is under ExitProbe::projects().

list(): array

Every project on your account.

use ExitProbe\Laravel\Facades\ExitProbe;

$result = ExitProbe::projects()->list();
$projects = $result['data']['projects'];
create(array $input): array

Create a project to group monitors under.

$result = ExitProbe::projects()->create(['name' => 'Production']);
$projectId = $result['data']['project']['id'];
get(int $id): array

Fetch one project.

$result = ExitProbe::projects()->get(3);
update(int $id, array $input): array

Rename a project.

ExitProbe::projects()->update(3, ['name' => 'Production (EU)']);
delete(int $id): array

Soft-delete — recoverable with restore().

ExitProbe::projects()->delete(3);
restore(int $id): array

Undo a delete().

ExitProbe::projects()->restore(3);
regenerateKey(int $id): array

Rotate the project's api_key. The old key stops working immediately.

$result = ExitProbe::projects()->regenerateKey(3);
$newKey = $result['data']['project']['api_key'];
clearChecks(int $id): array

Delete stored check history for every monitor in the project.

ExitProbe::projects()->clearChecks(3);
monitors(int $id, array $query = []): array

Monitors scoped to this project — same filters as monitors()->list().

$result = ExitProbe::projects()->monitors(3, ['status' => 'down']);

Monitors

Every method is under ExitProbe::monitors().

Listing & fleet status
list(array $query = []): array

List monitors, optionally filtered. status is up/down/degraded/flapping/ip_mismatch/unknown. data.summary always counts the whole filtered fleet. Send per_page (max 100) and optionally page to paginate; omit both to get everything.

$result = ExitProbe::monitors()->list(['per_page' => 25, 'page' => 1, 'range' => '7d']);

$result['data']['summary'];    // ['total' => 93, 'up' => 88, 'down' => 2, 'degraded' => 1, 'flapping' => 1, 'ip_mismatch' => 1, 'unknown' => 0, 'inactive' => 1]
$result['data']['pagination']; // ['page' => 1, 'per_page' => 25, 'total' => 93, 'last_page' => 4, 'has_more' => true]

foreach ($result['data']['monitors'] as $monitor) {
    echo $monitor['id'].' '.$monitor['name'].' '.$monitor['fleet_health']['status'].PHP_EOL;
}
Each row carries fleet_health, protocols_summary (per-protocol status and latency) and uptime_history (timeline bars + uptime %). Add defer_uptime => 1 and lean => 1 for a much lighter payload on big fleets.
listAll(array $query = []): array

Walks every page for you and returns all monitors. Uses lean + defer_uptime so a 200-server fleet stays fast.

$down = ExitProbe::monitors()->listAll(['status' => 'down']);
foreach ($down as $monitor) {
    echo $monitor['name'].' last checked '.$monitor['last_check_at'].PHP_EOL;
}
summary(array $query = []): array

How many servers are up, down, degraded, flapping, IP-mismatched or inactive — no rows, so it is cheap to poll from a status board.

$s = ExitProbe::monitors()->summary()['data'];
echo "{$s['down']} down, {$s['degraded']} degraded, {$s['flapping']} flapping, {$s['inactive']} inactive";
Adding a monitor
create(array $input): array

Create a monitor in your default project. See Protocol Reference for the exact per-protocol fields (wireguard_config, vless_uri, ikev2_target_host, etc).

$result = ExitProbe::monitors()->create([
    'name' => 'EU Exit Node',
    'expected_exit_ip' => '203.0.113.45',
    'check_interval_seconds' => 300,
    'wireguard_enabled' => true,
    'wireguard_config' => "[Interface]\nPrivateKey=...\n[Peer]\nPublicKey=...\nEndpoint=vpn.example.com:51820",
]);
$monitorId = $result['data']['monitor']['id'];
At least one {protocol}_enabled flag plus its config/URI is required, or this throws a 422 (ExitProbeException).
createForProject(int $projectId, array $input): array

Same as create(), but inside a specific project instead of your default one.

$result = ExitProbe::monitors()->createForProject(3, [
    'name' => 'US Exit Node',
    'vless_enabled' => true,
    'vless_uri' => 'vless://uuid@host:443?security=tls&sni=host#US-Node',
]);
Reading & managing a monitor
get(int $id): array

Fetch one monitor's full current state.

$result = ExitProbe::monitors()->get(42);
update(int $id, array $input): array

Change any subset of fields — e.g. enable a second protocol on an existing monitor.

ExitProbe::monitors()->update(42, [
    'check_interval_seconds' => 60,
    'hysteria_enabled' => true,
    'hysteria_uri' => 'hysteria2://password@host:443?sni=host&insecure=1',
]);
delete(int $id): array

Soft-delete — recoverable with restore().

ExitProbe::monitors()->delete(42);
restore(int $id): array

Undo a delete().

ExitProbe::monitors()->restore(42);
Running an on-demand check
checkNow(int $id): array

Trigger an immediate check across every probe assigned to this monitor. Checks run asynchronously — poll checkStatus() with the returned ids.

$result = ExitProbe::monitors()->checkNow(42);
$checkIds = $result['data']['check_ids']; // [9001, 9002, 9003]
checkStatus(int $id, array $checkIds): array

Poll the checks dispatched by checkNow(). data.all_complete flips to true once every probe has answered. (checkNowAndWait() below does this loop for you.)

do {
    $result = ExitProbe::monitors()->checkStatus(42, [9001, 9002, 9003]);
    $done = $result['data']['all_complete']; // $result['data']['checks'] holds each probe's result
    if (! $done) sleep(2);
} while (! $done);
History, uptime & analytics
checks(int $id, array $query = []): array

Paginated raw check history.

$result = ExitProbe::monitors()->checks(42, ['per_page' => 50, 'protocol' => 'wireguard']);
clearChecks(int $id): array

Delete this monitor's stored check history.

ExitProbe::monitors()->clearChecks(42);
uptime(int $id, array $query = []): array

Uptime percentage and latency history. range is 1h/6h/12h/24h/7d/30d/90d.

$result = ExitProbe::monitors()->uptime(42, ['range' => '7d']);
uptimeRegions(int $id, array $query = []): array

Same, broken down per probe region.

$result = ExitProbe::monitors()->uptimeRegions(42, ['range' => '30d']);
analytics(int $id, array $query = []): array

The full charting/analytics payload used by the monitor detail page in the dashboard.

$result = ExitProbe::monitors()->analytics(42, ['range' => '30d']);
status(int $id): array

A lightweight status probe — cheap to poll frequently from your own uptime page or a status badge.

$result = ExitProbe::monitors()->status(42);
echo $result['data']['status']; // 'up'
Everything about one monitor, bulk edits & waiting on a check
details(int $id, string $range = '24h', int $checks = 20): array

Everything the dashboard's monitor page shows — configuration, live status per protocol, uptime for the range, per-region uptime, the analytics behind the graphs, and the latest checks — gathered in one call.

$d = ExitProbe::monitors()->details(42, '7d', 50);

$d['status']['status'];            // 'up'
$d['uptime']['uptime_percentage']; // 99.93
$d['checks'];                      // latest 50 checks, newest first
checkNowAndWait(int $id, int $timeoutSeconds = 90, float $intervalSeconds = 1.5): array

Triggers a check and returns once every probe has answered (default timeout 90s — an IKEv2 handshake alone can take a minute). On timeout it returns completed => false instead of throwing.

$r = ExitProbe::monitors()->checkNowAndWait(42);

foreach ($r['checks'] as $c) {
    echo "{$c['protocol']} via {$c['probe_name']}: {$c['status']} ({$c['latency_ms']}ms)".PHP_EOL;
}
bulkUpdate(array $input): array

Change the check interval and/or probe assignment of many monitors at once. A field is only changed when you send it.

ExitProbe::monitors()->bulkUpdate([
    'monitor_ids' => [1, 2, 3],
    'check_interval_seconds' => 300,
]);

VPS servers

Every method is under ExitProbe::vps(). Register a server, get the one-time install command, then read the same live health data the dashboard shows.

create(array $input): array

Register a server (name, ip_address, optional project_id and location). The response carries the agent token and the one-time command to run on the server. The token is shown only now — store it, or rotate it later.

$result = ExitProbe::vps()->create(['name' => 'prod-db-1', 'ip_address' => '203.0.113.5']);

$result['data']['install_command']; // curl -sSL https://… | sudo bash -s -- -s … -t vps_… -r vps
$result['data']['agent_token'];     // vps_…
Run install_command once on the server (as root). The server then reports its own health every ~90 seconds.
list(array $query = []): array

Your servers with fleet counts. status is online/offline/unregistered; search matches name, IP or hostname. Send per_page/page to paginate.

$result = ExitProbe::vps()->list(['per_page' => 25]);

$result['data']['summary'];
// ['total' => 4, 'online' => 3, 'offline' => 1, 'unregistered' => 0, 'inactive' => 0, 'average_health_score' => 88, 'critical' => 1, 'excellent' => 2]
$result['data']['usage']; // ['count' => 4, 'max_custom_vps' => 10, 'can_add_custom_vps' => true]
listAll(array $query = []): array

Walks every page and returns all servers.

$offline = ExitProbe::vps()->listAll(['status' => 'offline']);
summary(array $query = []): array

Online / offline / unregistered counts and the average health score.

$s = ExitProbe::vps()->summary()['data'];
echo "{$s['online']} online, {$s['offline']} offline, avg health {$s['average_health_score']}";
get(int $id, array $query = []): array

Everything on the dashboard's server page: live CPU / memory / disk / swap / load, health score and grade, availability (24h / 7d / 30d), system information, top processes, alert thresholds, monthly bandwidth, the snapshot history for the charts, and recent incidents.

$result = ExitProbe::vps()->get(7, ['range' => '24h']);

$s = $result['data']['vps_server'];
echo "{$s['health_score']} ({$s['health_grade']}) cpu {$s['cpu_usage']}% mem {$s['memory_usage']}% disk {$s['disk_usage']}%";
$s['availability'];    // ['24h' => 100, '7d' => 99.9, '30d' => 99.95]
$s['top_processes'];   // heaviest processes right now
$result['data']['health_snapshots']; // chart history
$result['data']['incidents'];
range is 1h/6h/24h/7d/30d; limit (1–2000, default 500) bounds the number of snapshots, newest first.
history(int $id, array $query = []): array

Just the chart history and incidents — nothing else — for a window.

$result = ExitProbe::vps()->history(7, ['range' => '7d']);
getSummary(int $id): array

The current state without the (large) snapshot history — the cheap call for a status widget.

$result = ExitProbe::vps()->getSummary(7);
update(int $id, array $input): array

Rename, switch off, mute alerts, or change when a CPU / memory / disk alert fires. Thresholds are 50–100; threshold_duration_minutes is 5–1440 (how long a reading must stay over the line).

ExitProbe::vps()->update(7, [
    'cpu_threshold' => 85,
    'memory_threshold' => 90,
    'disk_threshold' => 90,
    'threshold_duration_minutes' => 30,
    'notifications_enabled' => true, // false mutes outbound alerts; incidents are still recorded
]);
regenerateToken(int $id): array

Rotate the agent token. The old token stops working immediately, so run the returned command on the server again.

$result = ExitProbe::vps()->regenerateToken(7);
echo $result['data']['install_command'];
delete(int $id): array

Remove a server and its history. Final — there is no restore.

ExitProbe::vps()->delete(7);
lookupIp(string $ip): array

Resolve the country and city for an IP before registering.

$info = ExitProbe::vps()->lookupIp('203.0.113.5')['data'];

Plan, limits & renewal

Read-only under ExitProbe::billing(). Changing or cancelling a plan is deliberately not possible with an API token — do that in the dashboard.

usage(): array

Your plan, every limit with how much of it you have used, wallet credit, and the renewal date.

$u = ExitProbe::billing()->usage()['data'];

echo "{$u['plan']['name']} - {$u['plan']['price']}/{$u['plan']['billing_interval']}";
$u['monitors'];   // ['used' => 41, 'limit' => 100, 'remaining' => 59, 'percentage' => 41, …]
$u['custom_vps']; // ['used' => 4, 'limit' => 10, …]
$u['manual_checks']['resets_at'];

$b = $u['billing'];
echo "{$b['plan_period_ends_at']} ({$b['days_until_renewal']} days), credit {$b['credit_balance']}";
days_until_renewal is negative once the paid period has lapsed, and null on a free plan.
current(): array

The plan with its full limits (allowed check intervals, data retention, feature flags) plus the same usage meters.

$c = ExitProbe::billing()->current()['data'];
$c['plan']['allowed_intervals'];
plans(): array

The public plan catalog, including add-on prices.

$plans = ExitProbe::billing()->plans()['data'];

Token abilities

A token created with no abilities has full access. To hand an integration only what it needs, pick abilities when creating the token (Settings → API Tokens):

monitors:read     monitors:write      # VPN monitors
projects:read     projects:write      # projects
vps:read          vps:write           # VPS servers (write = register, edit, delete, rotate token)
billing:read                          # plan, limits, usage, renewal date (read-only)
integrations:read integrations:write  # notification integrations

A token missing the ability a call needs gets a 403 ($e->status === 403 on the exception).

Endpoints without a typed method (incidents, server sources, escalation policies, …) are still reachable through the low-level escape hatch: ExitProbe::request($method, $path, $query = [], $body = null).