Administrate PHP SDK
The official PHP SDK for the Administrate.dev REST API.
Administrate.dev is AI Agency Management software. It is a monitoring and management platform for AI agencies running n8n and AI automation workflows across multiple clients. It provides a single dashboard to track every workflow, every client, every failure, and all LLM costs, so you can catch problems before clients do and prove the value of your automations.
Key platform features:
- Multi-instance monitoring -- See all n8n instances across every client in one place
- Error tracking -- Real-time failure detection with automatic error categorization
- LLM cost tracking -- Connect OpenAI, Anthropic, Azure, and OpenRouter accounts to attribute costs to specific clients
- Workflow insights -- Execution counts, success rates, and time-saved ROI reporting
- Sync health -- Know instantly when a data sync fails
- Webhooks & API -- Full programmatic access for custom integrations
Installation
Install via Composer:
composer require administrate/sdk
Requires PHP 8.1+.
Quick start
use Administrate\Administrate;
$client = new Administrate(apiKey: 'sk_live_...');
$account = $client->account->get();
echo "{$account->name} ({$account->plan})";
foreach ($client->clients->list() as $c) {
echo "{$c->name} ({$c->code})\n";
}
foreach ($client->executions->list(errorsOnly: true) as $execution) {
echo "{$execution->workflow_name}: {$execution->error_category}\n";
}
$costs = $client->llmCosts->summary();
echo "Total: $" . number_format($costs->data->summary->total_cost_cents / 100, 2);
Configuration
use Administrate\Administrate;
$client = new Administrate(
apiKey: 'sk_live_...',
baseUrl: 'https://...',
timeout: 30,
maxRetries: 3,
);
You can also pass any PSR-18 compatible HTTP client:
$client = new Administrate(
apiKey: 'sk_live_...',
client: $yourPsr18Client,
);
API reference
All API keys are created in Settings > Developers within Administrate.dev. Tokens have three permission levels: read, write, and full.
Account
$me = $client->account->me();
echo "{$me->token->name} ({$me->token->permission})";
echo "{$me->account->name} ({$me->account->plan})";
$account = $client->account->get();
$account = $client->account->update(
name: 'My Agency',
billingEmail: 'billing@example.com',
timezone: 'Australia/Brisbane',
);
Clients
Clients represent the companies you manage automations for.
foreach ($client->clients->list() as $c) {
echo "{$c->name} ({$c->code})\n";
}
$c = $client->clients->get('com_abc123');
echo "{$c->metrics->success_rate}% success, {$c->metrics->time_saved_minutes} min saved";
$c = $client->clients->create(
name: 'Acme Corp',
code: 'acme',
contactEmail: 'ops@acme.com',
timezone: 'America/New_York',
);
$c = $client->clients->update('com_abc123', notes: 'Enterprise tier');
$client->clients->delete('com_abc123');
Instances
Instances are n8n deployments connected to Administrate.
foreach ($client->instances->list() as $inst) {
echo "{$inst->name} ({$inst->sync_status})\n";
}
foreach ($client->instances->list(clientId: 'com_abc123', syncStatus: 'error') as $inst) {
echo "{$inst->name}: {$inst->last_sync_error}\n";
}
$inst = $client->instances->get('n8n_abc123');
echo "{$inst->metrics->executions_count} executions, {$inst->metrics->success_rate}% success";
$inst = $client->instances->create(
clientId: 'com_abc123',
name: 'Production n8n',
baseUrl: 'https://n8n.acme.com',
apiKey: 'n8n_api_key_here',
);
$client->instances->sync('n8n_abc123', syncType: 'all');
$client->instances->syncAll(syncType: 'workflows');
$inst = $client->instances->update('n8n_abc123', name: 'Staging n8n');
$client->instances->delete('n8n_abc123');
Workflows
foreach ($client->workflows->list(clientId: 'com_abc123', active: true) as $wf) {
echo "{$wf->name} (active: {$wf->is_active})\n";
}
foreach ($client->workflows->list(search: 'onboarding') as $wf) {
echo $wf->name . "\n";
}
$wf = $client->workflows->get('wfl_abc123');
echo "{$wf->metrics->success_rate}% success, {$wf->metrics->time_saved_minutes} min saved";
$wf = $client->workflows->update(
'wfl_abc123',
minutesSavedPerSuccess: 15,
minutesSavedPerFailure: 5,
);
Executions
Executions are read-only records of workflow runs synced from n8n.
foreach ($client->executions->list(
clientId: 'com_abc123',
status: 'failed',
startDate: '2025-01-01',
endDate: '2025-01-31',
) as $ex) {
echo "{$ex->workflow_name} - {$ex->status} ({$ex->duration_ms}ms)\n";
}
foreach ($client->executions->list(errorsOnly: true) as $ex) {
echo "{$ex->error_category}: {$ex->workflow_name}\n";
}
$ex = $client->executions->get('exe_abc123');
echo $ex->error_message;
Sync runs
foreach ($client->syncRuns->list(instanceId: 'n8n_abc123', status: 'failed') as $run) {
echo "{$run->sync_type} - {$run->status} ({$run->duration_seconds}s)\n";
}
$run = $client->syncRuns->get('syn_abc123');
foreach ($client->syncRuns->health() as $entry) {
echo "{$entry->instance_name} ({$entry->sync_status})\n";
echo " Workflows last synced: {$entry->workflows->last_synced_at}\n";
echo " Executions last synced: {$entry->executions->last_synced_at}\n";
}
Users
foreach ($client->users->list() as $user) {
echo "{$user->name} <{$user->email}> ({$user->role})\n";
}
$user = $client->users->get('usr_abc123');
$invitation = $client->users->invite(email: 'new@example.com', role: 'member');
echo $invitation->expires_at;
$user = $client->users->update('usr_abc123', role: 'admin');
$client->users->delete('usr_abc123');
Webhooks
foreach ($client->webhooks->list() as $wh) {
echo "{$wh->url} - " . implode(', ', $wh->events) . " (enabled: {$wh->enabled})\n";
}
$wh = $client->webhooks->create(
url: 'https://example.com/hook',
events: ['execution.failed', 'sync.failed'],
description: 'Slack failure alerts',
);
echo $wh->secret;
$wh = $client->webhooks->update('whk_abc123', enabled: false);
$wh = $client->webhooks->regenerateSecret('whk_abc123');
echo $wh->secret;
$client->webhooks->delete('whk_abc123');
API tokens
foreach ($client->apiTokens->list() as $token) {
echo "{$token->name} ({$token->permission}) - {$token->token_hint}\n";
}
$token = $client->apiTokens->create(
name: 'CI/CD Pipeline',
permission: 'read',
ipAllowlist: ['10.0.0.0/8'],
expiresIn: '90_days',
);
echo $token->token;
$token = $client->apiTokens->update('tok_abc123', name: 'Updated Name');
$client->apiTokens->delete('tok_abc123');
LLM providers
Connect your AI provider accounts to track costs.
foreach ($client->llmProviders->list() as $provider) {
echo "{$provider->name} ({$provider->provider_type}) - {$provider->sync_status}\n";
}
$provider = $client->llmProviders->get('llm_abc123');
echo "{$provider->metrics->total_cost_cents}c, {$provider->metrics->total_tokens} tokens";
$provider = $client->llmProviders->create(
name: 'OpenAI Production',
providerType: 'openai', // openai, anthropic, openrouter, or azure
apiKey: 'sk-...',
organizationId: 'org-...',
);
$client->llmProviders->sync('llm_abc123');
$provider = $client->llmProviders->update('llm_abc123', name: 'OpenAI Staging');
$client->llmProviders->delete('llm_abc123');
LLM projects
Projects are discovered automatically when syncing a provider. Assign them to clients to attribute costs.
foreach ($client->llmProjects->list('llm_abc123') as $project) {
echo "{$project->name}: {$project->total_cost_cents}c ({$project->client_name})\n";
}
$project = $client->llmProjects->update(
'llm_abc123', 'proj_456', clientId: 'com_abc123',
);
LLM costs
$costs = $client->llmCosts->summary();
echo "Total: $" . number_format($costs->data->summary->total_cost_cents / 100, 2) . "\n";
echo "Tokens: {$costs->data->summary->total_tokens}\n";
foreach ($costs->data->providers as $p) {
echo " {$p->name}: $" . number_format($p->cost_cents / 100, 2) . "\n";
}
foreach ($costs->data->models as $m) {
echo " {$m->model}: $" . number_format($m->cost_cents / 100, 2) . "\n";
}
foreach ($costs->data->daily as $day) {
echo " {$day->date}: $" . number_format($day->cost_cents / 100, 2) . "\n";
}
$costs = $client->llmCosts->summary(
startDate: '2025-01-01',
endDate: '2025-01-31',
);
foreach ($client->llmCosts->byClient()->data as $entry) {
echo "{$entry->name}: $" . number_format($entry->cost_cents / 100, 2) . "\n";
}
foreach ($client->llmCosts->byProvider()->data as $entry) {
echo "{$entry->name}: $" . number_format($entry->cost_cents / 100, 2) . "\n";
}
All ->list() methods return an IteratorAggregate that handles pagination automatically. By default, the API returns 25 items per page (max 100).
foreach ($client->clients->list() as $c) {
echo $c->name . "\n";
}
foreach ($client->clients->list(perPage: 100) as $c) {
echo $c->name . "\n";
}
$page = $client->clients->list(perPage: 10)->firstPage();
echo "{$page->meta->total} total, {$page->meta->total_pages} pages\n";
foreach ($page as $c) {
echo $c->name . "\n";
}
Error handling
The SDK raises typed exceptions for all API errors:
use Administrate\Administrate;
use Administrate\Exceptions\ApiException;
use Administrate\Exceptions\AuthenticationException;
use Administrate\Exceptions\NotFoundException;
use Administrate\Exceptions\RateLimitException;
use Administrate\Exceptions\ValidationException;
$client = new Administrate(apiKey: 'sk_live_...');
try {
$c = $client->clients->get('com_nonexistent');
} catch (NotFoundException $e) {
echo "Not found: {$e->getMessage()}";
} catch (AuthenticationException) {
echo 'Invalid API key';
} catch (RateLimitException $e) {
echo "Rate limited. Retry after {$e->retryAfter}s";
} catch (ValidationException $e) {
echo "Invalid params: " . json_encode($e->body);
} catch (ApiException $e) {
echo "API error {$e->statusCode}: {$e->getMessage()}";
}
Exception hierarchy:
AdministrateException | -- | Base exception for all SDK errors |
ApiException | Any non-2xx | Base for all HTTP API errors |
AuthenticationException | 401 | Invalid or missing API key |
PermissionDeniedException | 403 | Insufficient token permissions |
NotFoundException | 404 | Resource does not exist |
ValidationException | 422 | Invalid request parameters |
RateLimitException | 429 | Rate limit exceeded (has retryAfter) |
InternalServerException | 5xx | Server-side error |
ConnectionException | -- | Failed to connect to the API |
TimeoutException | -- | Request timed out |
All ApiException subclasses expose statusCode and body (parsed JSON or string).
Retries
The SDK automatically retries failed requests with exponential backoff:
- 429 (rate limited) -- Retries after the duration specified in the
Retry-After header
- 5xx (server errors) -- Retries with exponential backoff (0.5s, 1s, 2s, ...)
- Connection errors and timeouts -- Retried with the same backoff schedule
By default, the SDK retries up to 3 times. Set maxRetries: 0 to disable:
$client = new Administrate(apiKey: 'sk_live_...', maxRetries: 0);
Requirements
License
MIT