On this page
Callers, tenants, and member identity
A tenant is a customer or group in your application. MiniUp supplies authenticated caller identity, not an organization directory or a tenant-management API. Decide whether a tenant is one API key, one member, or a group before choosing storage.
Choose the right context
| API | What it means | Use it for |
|---|---|---|
env.MINIUP_MEMORY | Shared JSON storage for one Function and environment | Shared configuration; explicitly authorized team data |
env.MINIUP_CALLER | Frozen { id, type } for the verified caller | Checking whether caller identity exists; associating a request with its caller |
env.MINIUP_MEMORY.caller | Storage automatically scoped to that caller | Customer integration settings or individual member preferences |
env.MINIUP_USER | Trusted { id, email, name, role, siteId } for Site Members | Role checks and member-aware responses |
There is no env.MINIUP_TENANT binding. A query parameter, request header, or JSON field named tenantId is not proof of tenancy. Caller IDs are opaque and scoped to a Function. Do not use them to correlate identities between Functions.
One key per customer integration
- Choose API → API Key in the Function editor.
- Create a named key for each trusted integration, such as “Acme production”. Up to 20 keys can be active.
- Copy the secret when it is shown and deliver it through your established secure credential process. Keep it out of browser files.
- Store customer settings with
env.MINIUP_MEMORY.caller. MiniUp selects the namespace from the verified key; the request supplies no caller ID. - Rename a key to update its label. Rotate it to replace its secret while preserving identity and memory. Update the integration immediately because the previous secret stops working.
- Revoke a key to stop future authentication. Its audit history and stored caller memory are retained; an administrator can manage stored memory separately.
Two separate keys have separate caller memory, even if both belong to the same company. Sharing one key shares one identity and its data. Rotation preserves identity; creating a replacement key creates a new identity.
Member preferences and role checks
Link a Site Members Function to your site and allow GET and POST. This example uses private preferences per member, while returning only the member information the frontend needs:
export default {
async fetch(request, env) {
const user = env.MINIUP_USER;
if (!user || !env.MINIUP_CALLER) {
return Response.json({ error: "Sign in through the linked site" }, { status: 401 });
}
if (request.method === "GET") {
const preferences = await env.MINIUP_MEMORY.caller.get("preferences");
return Response.json({ name: user.name, role: user.role, preferences });
}
if (request.method === "POST") {
let input;
try { input = await request.json(); }
catch { return Response.json({ error: "Expected JSON" }, { status: 400 }); }
if (!input || !["light", "dark", "system"].includes(input.theme)) {
return Response.json({ error: "Invalid theme" }, { status: 400 });
}
await env.MINIUP_MEMORY.caller.set("preferences", { theme: input.theme });
return Response.json({ saved: true });
}
return new Response("Method not allowed", { status: 405, headers: { Allow: "GET, POST" } });
}
};Call it from a static page on the linked site (replace member-preferences with your Function slug):
const response = await fetch("/api/functions/member-preferences", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ theme: "dark" })
});
if (!response.ok) throw new Error("Could not save preferences");
const result = await response.json();For a shared administrative operation, check roles inside the Function, before reading or modifying protected shared data:
const user = env.MINIUP_USER;
if (!user) return new Response("Unauthorized", { status: 401 });
if (!["owner", "admin"].includes(user.role)) {
return new Response("Forbidden", { status: 403 });
}
// Only after this check: perform the administrative operation.owner, admin, editor, and viewer are site roles. An API-key caller or verified x402 payer does not automatically have a site role. Hiding a button is not authorization. Return only the fields needed by the page; do not expose credentials or unnecessarily return member email addresses.
Groups and organization tenants
Caller memory is per authenticated caller, not shared automatically across members of a company. For organization data, maintain membership and permissions in an appropriately protected Table or service. Resolve the organization from trusted identity, check access on every request, then scope data access to the authorized organization. Never concatenate an unverified tenant ID into a shared-memory key and treat that as authorization.
Preview, anonymous access, and payments
Preview and production memory are separate. A Site Member owner preview uses the owner’s caller identity in preview storage; other previews have no authenticated caller. Test API-key tenant separation through the published endpoint with separate keys.
Anonymous Public requests and free x402 routes have no MINIUP_CALLER; caller-memory methods throw. Verified x402 callers get an identity derived from the verified network and payer. Valid API keys and Site Members keep their own identity when bypassing payment. Payment settlement happens after execution, so a settlement error can follow an already-completed memory write.
See Memory methods, limits, and examples, access setup, and Site Members.
tenant · multi-tenant · MINIUP_CALLER · MINIUP_USER · caller memory · API keys