You are the platform. Your customers sign up, bring their own numbers and provider keys, configure their own agents, and you bill them for usage — all on top of TeleQuick Voice. This recipe is the backend that makes a tenant real: it provisions the tenant’s endpoints and data isolation, mints a scoped key for that tenant, publishes their agent config, and then drives calls with the tenant’s own key. Everything keys off one stable identifier — the org id. The per-tenant SIP realm (<orgId>.sip.telequick.dev), the analytics row policy, sealed credentials, and the usage account are all keyed on it, so a customer can rename their workspace without moving any infrastructure. For the conceptual model behind this code, read Building voice agent platforms first.

Two planes, two credentials

This backend uses two different credentials, and mixing them up is the one mistake that breaks tenancy:
  • A platform control-plane credential provisions tenants and mints keys. It is privileged — it can create resources for any org. Keep it in your backend’s secret store; never ship it to a browser or hand it to a customer.
  • A per-tenant scoped key drives one tenant’s calls. It resolves to exactly one org id and is refused (403) outside that tenant’s scope. This is the key you give a customer (or embed in a short-lived browser token).
The control plane lives at portal.telequick.dev; per-tenant call and audio traffic goes through the SDK, which talks to engine.telequick.dev and the media relay. Provisioning steps are idempotent — re-running the bootstrap backfills whatever was missed, so onboarding is safe to retry.

1. A thin control-plane client

Provisioning isn’t in the SDK — it is a privileged control-plane surface you call from your backend. This small wrapper posts to the control-plane API with your platform credential; every helper below is a real provisioning route.
// control-plane.ts — privileged; runs server-side only.
import { randomBytes } from "node:crypto";

export class ControlPlane {
  constructor(
    private readonly host = "https://portal.telequick.dev",
    private readonly adminKey = process.env.TELEQUICK_API_KEY!, // platform-admin credential
  ) {}

  private async rpc<T>(proc: string, input: unknown): Promise<T> {
    const res = await fetch(`${this.host}/api/trpc/${proc}`, {
      method: "POST",
      headers: {
        "content-type": "application/json",
        "authorization": `Bearer ${this.adminKey}`,
      },
      body: JSON.stringify(input),
    });
    if (!res.ok) throw new Error(`${proc} ${res.status}: ${(await res.text()).slice(0, 200)}`);
    const body = await res.json() as { result?: { data?: T }; error?: { message?: string } };
    if (body.error) throw new Error(body.error.message ?? `${proc} failed`);
    return body.result!.data as T;
  }

  /** Create the org's endpoints + data isolation. Idempotent. */
  bootstrap(orgId: string, adminToken: string) {
    return this.rpc<{ ok: true; provisioned: unknown }>("admin.bootstrapOrg", {
      orgId, adminToken,
    });
  }

  /**
   * Register a scoped key for one tenant. You generate the token value; the
   * control plane binds it to `orgId` with the given scopes. Returns nothing
   * secret — you already hold the token, so hand it to the tenant now.
   */
  async mintTenantKey(orgId: string, scopes: string[]): Promise<string> {
    const token = "sk_" + randomBytes(24).toString("hex");
    await this.rpc<{ ok: true }>("admin.publishServiceAccount", {
      orgId, token, scopes, isGlobalAdmin: false,
    });
    return token;
  }

  revokeTenantKey(orgId: string, token: string) {
    return this.rpc<{ ok: true }>("admin.revokeServiceAccount", { orgId, token });
  }

  /** Point the tenant's SIP trunk at their realm. */
  addTrunk(orgId: string, trunk: Record<string, unknown>) {
    return this.rpc<{ ok: true }>("admin.addTrunk", { orgId, trunk });
  }

  /** Map a DID to a trunk + agent for inbound routing. */
  upsertNumber(orgId: string, number: Record<string, unknown>) {
    return this.rpc<{ ok: true }>("admin.upsertPhoneNumber", { orgId, number });
  }

  /** Seal a tenant's provider key (BYO OpenAI / Deepgram / ElevenLabs / …). */
  setProviderCredentials(orgId: string, provider: string, payload: Record<string, string>) {
    return this.rpc<{ ok: true }>("admin.setProviderCredentials", { orgId, provider, payload });
  }

  /** Publish a saved agent's pipeline config to the engine. */
  hydrateAgent(orgId: string, agentId: string) {
    return this.rpc<{ ok: true; ready: boolean; missing_providers: string[] }>(
      "admin.hydrateAgent", { orgId, agentId },
    );
  }
}

2. Onboard a tenant

One call to bootstrap fans out the per-tenant resources: the DNS labels (<orgId>.sip / <orgId>.webrtc / <orgId>) that resolve to the engine, the isolated analytics role with a row policy pinned to tenant_id, the usage account, and the tenant’s entitlement. Then you register the tenant’s trunk, map their DIDs, seal any bring-your-own provider keys, publish their agent, and mint the scoped key their backend will use.
1

Bootstrap the org

Creates endpoints + data isolation, keyed on the org id.
2

Register trunk + numbers

Point their SIP trunk at their realm; map DIDs to it.
3

Seal provider keys

Store BYO keys sealed under the org (optional — they can inherit yours).
4

Publish the agent

Hydrate the saved agent config to the engine.
5

Mint a scoped key

Hand the tenant a key bound to their org id.
import { ControlPlane } from "./control-plane";

const cp = new ControlPlane();

export async function onboardTenant(orgId: string, opts: {
  trunkId: string;
  sipHost: string;          // carrier SIP host
  did: string;              // E.164 the tenant answers on
  agentId: string;          // an agent the customer already saved
  openaiKey?: string;       // optional BYO provider key
}): Promise<{ orgId: string; tenantKey: string }> {
  // 1. Endpoints + isolation. Idempotent — safe to re-run on retry.
  const adminToken = "at_" + crypto.randomUUID().replace(/-/g, "");
  await cp.bootstrap(orgId, adminToken);

  // 2. Trunk + DID. The engine derives tenant_id from the INVITE's DNS label,
  //    so the trunk just needs to target this org's realm.
  await cp.addTrunk(orgId, {
    trunk_id: opts.trunkId,
    direction: "inbound",
    realm: "external",
    external_sip_ip: opts.sipHost,
    media_mode: "proxy",
    inbound_rule: "ai_bidirectional_stream",
  });
  await cp.upsertNumber(orgId, {
    e164: opts.did,
    trunk_id: opts.trunkId,
    agent_id: opts.agentId,
    provider: "manual",
  });

  // 3. BYO provider key (sealed under the org). Skip to inherit the platform's.
  if (opts.openaiKey) {
    await cp.setProviderCredentials(orgId, "openai", { api_key: opts.openaiKey });
  }

  // 4. Publish the agent's pipeline config to the engine.
  const pub = await cp.hydrateAgent(orgId, opts.agentId);
  if (!pub.ready) {
    console.warn(`agent ${opts.agentId} not ready — missing:`, pub.missing_providers);
  }

  // 5. Scoped key for the tenant's backend. Least privilege.
  const tenantKey = await cp.mintTenantKey(orgId, ["voice:read", "voice:write"]);
  return { orgId, tenantKey };
}
hydrateAgent publishes an agent your customer already authored (prompt, providers, voice, turn-detection, tools). The agent-authoring model — the pipeline config each tenant edits — lives in runtime configuration; provider-key resolution order (per-agent → per-tenant → platform fallback) is covered in BYO ASR/LLM/TTS.

3. Drive calls with the tenant’s key

Now switch planes. To place or manage a call for a tenant, instantiate the SDK with that tenant’s scoped key and org id. The org id is the boundary the control plane enforces on every request — one tenant’s key can never address another tenant’s calls, agents, or audio.
import { Voice } from "@telequick/sdk/voice";

// Look up the tenant's key from your own store (you saved it at onboarding).
function voiceFor(orgId: string, tenantKey: string): Voice {
  return new Voice({
    baseUrl: "https://engine.telequick.dev",
    apiKey:  tenantKey,   // scoped to this org only
    orgId,
  });
}

// Place an outbound call on the tenant's trunk with their agent attached.
// The engine wires the audio bridge on answer — no bridge in your process.
export async function callForTenant(orgId: string, tenantKey: string, args: {
  to: string; from: string; trunkId: string; agent: string;
}) {
  const v = voiceFor(orgId, tenantKey);
  const call = await v.calls.originate({
    to: args.to,
    from: args.from,
    trunkId: args.trunkId,
    agent: args.agent,
    ringTimeoutSec: 25,
  });

  // Poll until it leaves the ringing states.
  for (;;) {
    const c = await v.calls.get({ sid: call.sid });
    if (c.status === "in_progress") return { sid: call.sid, status: c.status };
    if (c.status === "failed" || c.status === "no_answer") {
      return { sid: call.sid, status: c.status };
    }
    await new Promise((r) => setTimeout(r, 500));
  }
}
Inbound calls need no per-call code at all: because you mapped the DID to the agent at onboarding, an INVITE to <orgId>.sip.telequick.dev is routed by its leftmost DNS label to the tenant, resolved to the agent, and answered by the engine. Your backend only gets involved for outbound, transfers, or supervision.

4. Wire it to an HTTP surface

Wrapping the two functions above in a couple of routes gives you a complete onboarding-and-dial backend. Guard the provisioning route with your own staff auth — it holds the platform credential — and scope the dial route to the authenticated tenant.
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { onboardTenant, callForTenant } from "./platform";
import { lookupTenantKey } from "./store"; // your own tenant→key store

const app = new Hono();

// Privileged: your staff / signup flow only.
app.post("/tenants", async (c) => {
  const body = await c.req.json();
  const { orgId, tenantKey } = await onboardTenant(body.orgId, body);
  // Persist tenantKey in YOUR store; return an id, not the raw key, to the UI.
  await lookupTenantKey.save(orgId, tenantKey);
  return c.json({ orgId, provisioned: true });
});

// Per-tenant: authenticate the caller as a member of :orgId first (your auth).
app.post("/tenants/:orgId/calls", async (c) => {
  const orgId = c.req.param("orgId");
  const key = await lookupTenantKey.get(orgId);
  if (!key) return c.json({ error: "unknown tenant" }, 404);
  const result = await callForTenant(orgId, key, await c.req.json());
  return c.json(result);
});

serve({ fetch: app.fetch, port: 8787 });

Least-privilege scopes

Mint the narrowest scope set each consumer needs. Scopes are product:action strings the control plane checks per endpoint; a request missing the required scope is refused (403).
voice:read
scope
Read call state and history — status polling, listing, reports. Give this to a dashboard or a read-only integration.
voice:write
scope
Originate, transfer, and hang up calls; attach agents. The scope a tenant’s call-driving backend needs.
voice:*
scope
All voice actions. Use sparingly; prefer the two explicit scopes above.
For a customer-facing dashboard, don’t embed a long-lived tenant key in the browser. Mint a short-lived token per browser session with only the scopes that session needs — the supervisor-attach and browser-media legs already work this way. Revoke with revokeTenantKey when a customer offboards or rotates.

Metering is automatic

You don’t write metering code — the engine attributes usage per tenant as calls run. On each teardown it emits a CDR (Q.850 cause + duration, keyed by call_sid) rated against the tenant’s usage account, and it accounts media bytes per tenant per modality into a per-minute rollup. Both are keyed on the same org id, so a tenant’s spend and bandwidth are readable the moment their first call clears. See dashboards and call traces.

Building voice agent platforms

The concepts behind this code — tenancy, isolation, and metering.

Runtime configuration

The per-agent pipeline config each tenant authors.

SIP trunking

Register a tenant’s trunk against their realm.

Contact-center platform

Add ACD, skills, and supervisor tooling on top of tenancy.