HTTP, WebSocket and direct calls
Call one action from HTTP, a socket client or your own backend code, and know what each path requires
Call an action over HTTP, through a socket client or within your backend using server.invoke. Each path runs the action’s authentication, hooks, validation and handler.
When to use each path
| Path | Use it for | Requires |
|---|---|---|
HTTP POST /api/services | Browsers, mobile apps, curl and any HTTP client | intent, service, action and payload in the body |
Socket client invoke | Clients that already hold a socket connection for topics | An authenticated socket connection |
server.invoke | Jobs, scheduled work and other code in the same backend | Credentials for protected actions |
HTTP
Every operation is a POST to the services endpoint. The intent field selects execute, explore or schema:
curl -s -X POST http://localhost:8000/api/services \
-H "Content-Type: application/json" \
-d '{"intent":"execute","service":"tasks","action":"list","payload":{}}'A successful call returns status: true with the handler's object as data:
{
"status": true,
"message": "Action completed.",
"data": {
"tasks": []
}
}A failed call returns status: false and HTTP 400. Read the status field as well as the HTTP status.
Socket client
The dedicated socket client sends invoke over the same connection that receives topic messages. Use the same payload types as the HTTP client:
const changed = await socket.invoke({
service: "orders",
action: "set-status",
payload: { orderId: orderId, status: "packed" },
});
if (changed.isErr) {
console.error(changed.error, changed.details);
return;
}
console.log(changed.value);Protected actions use the identity that the server verified when the socket connected. See sockets and typed messages for the connection setup.
Direct calls with server.invoke
server.invoke runs an action in the same process without a network request. It is the right call from a scheduled job or from backend code that already has a server instance:
const result = await server.invoke({
service: "orders",
action: "create",
payload: { customerId: customerId, total: 0 },
requestId: "job-create-order-1",
});
if (result.isErr) {
return Err(result.error);
}
return Ok(result.value);The optional fields are:
| Field | Purpose |
|---|---|
requestId | Correlates logs and telemetry for this call. |
signal | Cancels the call. Nested calls inherit it. |
credentials | Headers to verify for a protected action. Required when the action is protected. |
session | Trusted application session for the call. It does not skip JWT verification. |
clientKey | Identity used for rate limits. |
Calling a protected action without credentials returns Err("Authentication required: no request context available"). Pass the verified headers, as the socket transport does, or call an unprotected action instead.
An action with visibility: { rpc: false } cannot be called through server.invoke. Set rpc.enabled: false on the server to turn the direct interface off entirely.
Limits
- Timeouts apply to HTTP and direct calls through
execution.timeoutMs. A timed-out call stops waiting, but code that ignores its signal keeps running. - Writes made before a timeout or a failure are not rolled back.
- The socket client has a finite acknowledgement timeout. A timeout does not prove that the write did not commit.
Common mistakes
- Sending a multipart upload to a JSON action, or a JSON body to an upload action. See file uploads.
- Calling
server.invokefor a protected action without credentials. - Passing a client-supplied
sessionintoserver.invokeand expecting it to replace JWT verification. - Reading
valuebefore checkingisErron the client.
See also
- Production operations explains direct calls, cancellation and correlation IDs.
- Sockets and typed messages describes the socket contract.
- Testing actions uses
server.invokein tests.