Calling the API In Process
Code that already runs inside an installation can call any endpoint without HTTP, a key or a network hop.
Overview
Every endpoint sample in this documentation carries a fourth tab: PHP (Internal). That tab is not a shortcut for the same HTTP request. It is a second entry point into the same resource. It is meant for code living inside the installation: a module, a cron handler, a hook listener, a theme.
The reason to use it is not speed alone. Calling your own installation over HTTP means issuing a key to yourself and spending that key's rate budget. It also depends on the web server being reachable from itself. The in-process call has none of those and returns the exact same envelope. The two are interchangeable at the point where you read the answer.
It is a trusted context by definition. Authentication, permissions, the rate limiter, the abuse guard, idempotency and the request log belong to the HTTP layer. None of them run here. Whatever you pass is executed.
Reference
The Signature
['data' => …, 'meta' => …] and failure gives ['error' => ['code', 'message', 'details'?]]. It never throws for an API-level failure and never prints anything.
Group/Action — the same string the documentation and the permission checkbox use. Prefix it with client: to reach the client surface.
page and limit go here, not in $input.
How $input Splits
A route such as services/{id}/addons has one placeholder. Passing ['id' => 5, 'name' => 'Backup'] sends 5 as the path parameter and leaves ['name' => 'Backup'] as the body. You never build the URL yourself, which is why a route change does not break your call.
The Client Surface
An HTTP call to the client surface takes the account from the key. There is no key here, so the account has to be named: pass owner_id in $input and prefix the action with client:. It is removed from the input before dispatch, so it never reaches the resource as a body field.
Omitting it is refused rather than guessed. The call returns owner_required and nothing runs.
What Comes Back on Failure
Group/Action on that surface. A typo lands here, and so does an admin action called with a client: prefix.
owner_id.
Example
use WISECP\Api\Kernel;
// admin surface, a list with query options
$res = Kernel::internal('Clients/GetClients', [], ['page' => 1, 'limit' => 50, 'status' => 'active']);
if (isset($res['error'])) {
\Logger::warning('client sync failed: ' . $res['error']['code']);
return;
}
foreach ($res['data'] as $client) { /* ... */ }
$more = (int) ($res['meta']['next_page'] ?? 0);
// path parameter + body in one array
$upd = Kernel::internal('Clients/UpdateClient', ['id' => 42, 'company_name' => 'Acme Ltd']);
// client surface: the account is named, not inferred
$me = Kernel::internal('client:Account/GetMe', ['owner_id' => 42]);
Pitfalls
An HTTP caller is filtered by its key's permissions before a resource is reached. An in-process call is not filtered at all, because the caller is the installation. If your code runs on behalf of a person, decide what that person may do before you call. Never let a request value choose the action or the target account.
The name is split at its first slash, and a module group already contains one: Module:Addons/AcmeSync. The split produces the wrong pair and the call answers endpoint_not_found. Code inside the installation should call the module's own class or operation directly; the endpoint exists for outside callers.
The API log records HTTP traffic. An in-process call leaves no row there, so an integration that mixes both will show only half its activity to the operator. If a background job needs an audit trail, write one of your own.
The demo guard refuses writes at the HTTP layer. This path is below it, so a cron handler or a hook listener keeps writing on an installation in demo mode. That is deliberate: the installation's own machinery has to keep running. It is worth remembering when a demo shows data a demo should not have.
Related Articles
Thanks for your feedback!
Our support team is here around the clock for anything you can't find above.