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/laravelThe service provider and ExitProbe facade are auto-discovered. Publish the config if you want to tweak defaults:
php artisan vendor:publish --tag=exitprobe-configThen set your API token (Settings → API Tokens in your dashboard):
EXITPROBE_API_KEY=your-tokenConfiguration
// 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(): arrayEvery project on your account.
use ExitProbe\Laravel\Facades\ExitProbe;
$result = ExitProbe::projects()->list();
$projects = $result['data']['projects'];create(array $input): arrayCreate a project to group monitors under.
$result = ExitProbe::projects()->create(['name' => 'Production']);
$projectId = $result['data']['project']['id'];get(int $id): arrayFetch one project.
$result = ExitProbe::projects()->get(3);update(int $id, array $input): arrayRename a project.
ExitProbe::projects()->update(3, ['name' => 'Production (EU)']);delete(int $id): arraySoft-delete — recoverable with restore().
ExitProbe::projects()->delete(3);restore(int $id): arrayUndo a delete().
ExitProbe::projects()->restore(3);regenerateKey(int $id): arrayRotate 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): arrayDelete stored check history for every monitor in the project.
ExitProbe::projects()->clearChecks(3);monitors(int $id, array $query = []): arrayMonitors scoped to this project — same filters as monitors()->list().
$result = ExitProbe::projects()->monitors(3, ['status' => 'down']);Monitors
Every method is under ExitProbe::monitors().
list(array $query = []): arrayList 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;
}listAll(array $query = []): arrayWalks 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 = []): arrayHow 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";create(array $input): arrayCreate 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'];createForProject(int $projectId, array $input): arraySame 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',
]);get(int $id): arrayFetch one monitor's full current state.
$result = ExitProbe::monitors()->get(42);update(int $id, array $input): arrayChange 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): arraySoft-delete — recoverable with restore().
ExitProbe::monitors()->delete(42);restore(int $id): arrayUndo a delete().
ExitProbe::monitors()->restore(42);checkNow(int $id): arrayTrigger 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): arrayPoll 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);checks(int $id, array $query = []): arrayPaginated raw check history.
$result = ExitProbe::monitors()->checks(42, ['per_page' => 50, 'protocol' => 'wireguard']);clearChecks(int $id): arrayDelete this monitor's stored check history.
ExitProbe::monitors()->clearChecks(42);uptime(int $id, array $query = []): arrayUptime 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 = []): arraySame, broken down per probe region.
$result = ExitProbe::monitors()->uptimeRegions(42, ['range' => '30d']);analytics(int $id, array $query = []): arrayThe full charting/analytics payload used by the monitor detail page in the dashboard.
$result = ExitProbe::monitors()->analytics(42, ['range' => '30d']);status(int $id): arrayA 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'details(int $id, string $range = '24h', int $checks = 20): arrayEverything 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 firstcheckNowAndWait(int $id, int $timeoutSeconds = 90, float $intervalSeconds = 1.5): arrayTriggers 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): arrayChange 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): arrayRegister 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_…list(array $query = []): arrayYour 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 = []): arrayWalks every page and returns all servers.
$offline = ExitProbe::vps()->listAll(['status' => 'offline']);summary(array $query = []): arrayOnline / 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 = []): arrayEverything 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'];history(int $id, array $query = []): arrayJust the chart history and incidents — nothing else — for a window.
$result = ExitProbe::vps()->history(7, ['range' => '7d']);getSummary(int $id): arrayThe current state without the (large) snapshot history — the cheap call for a status widget.
$result = ExitProbe::vps()->getSummary(7);update(int $id, array $input): arrayRename, 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): arrayRotate 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): arrayRemove a server and its history. Final — there is no restore.
ExitProbe::vps()->delete(7);lookupIp(string $ip): arrayResolve 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(): arrayYour 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']}";current(): arrayThe 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(): arrayThe 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 integrationsA token missing the ability a call needs gets a 403 ($e->status === 403 on the exception).
ExitProbe::request($method, $path, $query = [], $body = null).