How-to guide

Invoke methods from a client

Call methods synchronously or asynchronously, target any, all or a specific worker, filter by tags, and receive results by callback.

Distro Current 4 min read Updated

Prerequisites

  • A registered client and its credentials

Note

Kiwi Distro is currently in private preview. Interfaces and behaviour described here can change before general availability. Contact Kiwify Labs for access.

Clients authenticate with OAuth2 client credentials (a JWT access token that the SDK refreshes before it expires) and call methods addressed as service/method.

Synchronous request#

Choose your language. The choice applies to every sample on the site and is remembered in your browser.

.NET
using Kiwify.Kiwi.Distro.Client.Native.Core;

var factory = new MiddlewareClientFactory(middlewareUrl, middlewareCaUrl);
factory.CreateAuthProviderBuilder().SetClientCredentials(clientId, clientSecret).Build();
factory.CreateHttpBuilder().Build();

var res = await factory.CreateMethodRequestHandler("billing/charge")
    .AttachParameters(new Dictionary<string, object> { ["amount"] = 10 })
    .InvokeAnyAsync();
if (res.IsSuccess) Console.WriteLine(res.Result);
Java
import kiwify.kiwi.distro.client.*;
import java.util.Map;

KiwiClient client = KiwiClient.builder()
    .middlewareUrl(middlewareUrl).clientId(clientId).clientSecret(clientSecret)
    .connect();

Map<String, Object> res = client.method("billing/charge")
    .params(Map.of("amount", 10)).invokeAny();
if ("success".equals(res.get("status"))) System.out.println(res.get("result"));
Node.js
import { KiwiClient } from "@kiwify/kiwi-distro-client";

const client = await KiwiClient.connect({ middlewareUrl, clientId, clientSecret });

const res = await client.method("billing/charge").params({ amount: 10 }).invokeAny();
if (res.status === "success") console.log(res.result);
Python
from kiwify_kiwi_distro_client import KiwiClient

client = KiwiClient.connect(
    middleware_url=middleware_url, client_id=client_id, client_secret=client_secret)

res = client.method("billing/charge").params(amount=10).invoke_any()
if res["status"] == "success":
    print(res["result"])

A method that ran and failed is not a transport error: it comes back as status: "failed" with an error. Real errors (bad request, authorisation failure, no capable worker, a timeout in the backing store) are raised as exceptions (HTTP 4xx or 503).

Choose a target#

Target Meaning
any (default) One available worker for the service
all Every worker registered for the service. The result lists each worker's status, and the overall status is success, partial or failed
<worker-id> A specific worker
.NET
var all = await factory.CreateMethodRequestHandler("cache/flush").InvokeAllAsync();
// all.Status, all.Results: one entry per worker
Java
Map<String, Object> all = client.method("cache/flush").invokeAll();   // { status, results: [...] }
Node.js
const all = await client.method("cache/flush").invokeAll();   // { status, results: [...] }
Python
all_results = client.method("cache/flush").invoke_all()   # {"status", "results": [...]}

Filter by tags#

filters is an AND-combined list of tag criteria with operators eq, ne, in, nin and like; value is always an array:

json
{ "method": "report/build", "filter": [ { "tag": "region", "operator": "eq", "value": ["eu"] } ] }

Fire and forget#

submit returns a request id immediately. Collect the result later, or give a callback URL that the middleware calls when it is ready:

.NET
var submit = await factory.CreateMethodSubmitHandler("report/build")
    .AttachParameters(new Dictionary<string, object> { ["month"] = "2026-09" })
    .InvokeAsync("any");
var id = submit.Value!;

var results = factory.CreateMethodResultHandler();
var output = await results.GetMethodResultAsync(id);
while (output.IsPending) output = await results.GetMethodResultAsync(id);
Java
String id = client.method("report/build").params(Map.of("month", "2026-09")).submitAny();
Map<String, Object> out = client.result(id);
while ("pending".equals(out.get("status"))) out = client.result(id);
Node.js
const id = await client.method("report/build").params({ month: "2026-09" }).submitAny();
let out = await client.result(id);
while (out.status === "pending") out = await client.result(id);
Python
rid = client.method("report/build").params(month="2026-09").submit_any()
out = client.result(rid)
while out["status"] == "pending":
    out = client.result(rid)

Options: a ttl (for example 30S), callback and callback_timeout, a feedback_callback for progress, and version to pin a service version.

From the CLI#

bash
kiwi distro request-method --method billing/charge --params amount=10 --target any --output json
kiwi distro submit-method  --method report/build --params "month='2026-09'" --callback-url https://example.com/hook
kiwi distro pick-method-result --reference-id <id>

--params takes key='str',key=42,flag=true; use --params-file for a JSON object. --filters takes a JSON array, --service-version pins a version, and --output is table, json or raw.

Read progress#

bash
kiwi distro list-feedback --request-id <id>

SDKs#

See Client and worker SDKs for installation and configuration in each language. In the Java, Node.js and Python clients the fluent builder is the same: client.method("svc/method") then .params(...), .filter(...), .ttl(...), .version(...), and .invokeAny(), .invokeWorker(id) or .invokeAll() (request), or .submitAny(), .submitWorker(id) or .submitAll() (submit). The .NET client uses AttachParameters, SetTtl, SetVersion and AttachFilters with InvokeAnyAsync, InvokeWorkerAsync, InvokeAllAsync and InvokeAsync. All four clients have the same admin surface (services, clients, workers, secrets, sidecar, scripts, feedback and groups).

Expected result

  • Calls return results, or a reference id you can collect later