Calling the API In Process

7 views Markdown

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

Kernel::internal($groupAction, $input = [], $query = []): array Static. Returns the same payload an HTTP call would. Success gives ['data' => …, 'meta' => …] and failure gives ['error' => ['code', 'message', 'details'?]]. It never throws for an API-level failure and never prints anything.
$groupAction The endpoint's own name, Group/Action — the same string the documentation and the permission checkbox use. Prefix it with client: to reach the client surface.
$input One flat array holding both the path parameters and the body. Keys whose name matches a placeholder in the route pattern are lifted out as path parameters, in pattern order. Everything left over becomes the request body.
$query The query string, as an array. Filters, sorting, 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

endpoint_not_found No endpoint answers that Group/Action on that surface. A typo lands here, and so does an admin action called with a client: prefix.
owner_required A client call without owner_id.
validation_failed · not_found · … Whatever the resource itself refuses, in the same shape and with the same code as over HTTP.
server_error An unexpected failure. The detail goes to the error log, not into the returned message, unless the installation is in debug.

Example

both surfaces, from a module
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

No permission is checked here, so check your own

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.

Module endpoints cannot be reached this way

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.

Nothing is written to the request log

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.

Demo mode does not stop it

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.

Was this helpful?

Thanks for your feedback!

Still Need Help?

Our support team is here around the clock for anything you can't find above.