Writing serverless functions
A serverless function is your own code running on the accelerator. Reach for
one when something can’t live in the client: a secret you can’t ship to the
browser, a call to a third-party API, server-side validation, or logic an
agent calls as a tool. This page is the authoring
playbook — the client.functions reference has the full
management API.
The module shape
Section titled “The module shape”A function is a single ES module that default-exports a fetch handler — the
same shape as the request you’d write for any edge runtime:
export default { async fetch(request) { const { name } = await request.json().catch(() => ({})); return Response.json({ greeting: `Hello, ${name ?? "world"}!` }); },};It owns its own routing and CORS — branch on new URL(request.url).pathname and
request.method to expose multiple routes from one function.
Deploy it
Section titled “Deploy it”Deploy with the SDK or the CLI. The name is a DNS-safe slug —
it becomes the function’s subdomain label.
const { config } = await client.functions.deploy(appId, { name: "greet", displayName: "Greeter", code: greetSource, // the module above, as a string triggers: { http: { enabled: true } },});// Reachable at https://greet--<app-slug>.fns.<zone>/Call it from a client
Section titled “Call it from a client”client.functions.invoke calls an HTTP-triggered function and parses its JSON.
The SDK attaches the caller’s credentials automatically — the app key, and
the user’s session when one is signed in — so you don’t assemble the URL or wire
up auth headers yourself:
const out = await client.functions.invoke<{ greeting: string }>( { name: "greet", slug: "myapp" }, { body: { name: "Ada" } }, // a body implies POST);The platform passes the session token through to the function. To act as the user, verify it server-side and trust nothing the client claims:
const token = request.headers.get("X-Muhkoo-Session");const verify = await fetch("https://api.muhkoo.dev/api/auth/verify", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ token }),});if (!verify.ok) return new Response("Sign in required", { status: 401 });const { commitment } = await verify.json(); // the verified callerThis is the core “authenticated proxy” pattern: the browser calls the function with its session, the function verifies it and then uses a secret (an admin key, a third-party token) that never reaches the browser. Bake secrets in at deploy time rather than shipping them client-side.
Bind a function to a Space
Section titled “Bind a function to a Space”A function can also run on messages in a Space — the same model as an agent, but your code is the brain. It receives the decrypted message; a non-empty response body is posted back as a reply.
await client.functions.update(appId, fnId, { triggers: { http: { enabled: true }, space: { match: [{ type: "keyword", pattern: "/weather" }] } },});await client.functions.enable(appId, fnId, channelSpaceId); // per-Space opt-in// In the function, handle the Space event:if (request.headers.get("X-Muhkoo-Event") === "space.message") { const { text } = await request.json(); return Response.json({ reply: `You said: ${text}` }); // posted to the Space}Functions are per-environment
Section titled “Functions are per-environment”Every function exists independently in test and production — the same way
hosting and config do. Which one you touch is set by the environment of the key
you deploy with (mk_test_sk_* → test) or the portal’s env selector. Iterate in
test, then promote test → production in place once you’re happy. See
Environments.
- Deploy to test and exercise it at
<name>--<slug>.test.fns.<zone>. - When it’s right, promote — production functions are upserted by name.
- Production traffic is never touched until you promote.
Where to go next
Section titled “Where to go next”client.functionsreference — every method, the fullFunctionConfig, the Space-event contract, and caps/billing.- Building agents — let an AI agent call your functions as tools.
- Environments & deploy — the test → production model.