Skip to content

Developer quickstart (TenancyEngine)

Add TenancyEngine to an ISV backend in the same first-pass flow developers expect from Supabase, Firebase, or Vercel docs: create an app, mint a server-side key, install the SDK, read runtime context, record usage, and verify webhooks.

Runtime keys are server-side credentials. Do not ship an organization API key in browser JavaScript, mobile apps, or public source.

1. Create an application

  1. Open the TenancyEngine console.
  2. Go to Applications -> Create application.
  3. Copy the application slug, for example vectralabel.
  4. Open Environments and confirm the environment you will call, usually Development locally.

Local development uses:

  • SaaSRuntime API: https://auth.lab.saasruntime.com
  • Console: https://console.lab.tenancyengine.com
  • Docs: https://docs.lab.tenancyengine.com

Production apps should use the environment-specific authority/API base URL shown in the application runtime context.

2. Create an organization API key

Open API keys and create a key for your organization. Give the key only the scopes needed by your backend:

NeedScope
Read runtime context and application stateapplications.read
Read tenant state and usagetenants.read
Create tenants or consume/release usagetenants.write
Send analytics eventsanalytics:write
Local MCP automationmcp.full

Store the key in your secret manager. TenancyEngine shows the plain value once.

3. Pick a key tier, then install an SDK

Organization API keys come in two binding tiers, plus a reserved admin tier no app key should ever hold:

TierScopeUse it for
App-scopedBound to exactly one application; every call is checked against that bindingA single app's own backend, smaller blast radius if the key leaks. Default choice.
Org-wideWorks across every application your organization ownsA backend that manages multiple TenancyEngine apps (e.g. an agency, or a platform team)
Admin (admin/admin.*)Full org scope; app-scoped keys can never hold itPlatform/org administration only, not a normal integration credential

Full SDK list, install commands, and verified quickstarts for every language and platform (.NET, TypeScript, Go, Python, Java, PHP, Ruby, browser, and all five mobile auth SDKs): SDK reference. Short version for the two most complete SDKs:

TenancyEngine.Sdk (.NET) is published -- GitHub Packages feed nuget.pkg.github.com/sathish4000, versions 1.0.0/1.0.1. @tenancy-engine/sdk (TypeScript) is not yet published to npm (verified: 404 on the public registry) -- build it from source (cd packages/tenancy-engine-sdk && npm ci && npm run build) and consume it as a local/file: dependency until it ships. For any other language, use the package source under D:\repos\tenancy-platform\packages\ and ask the platform owner to publish it.

TypeScript

bash
npm install @tenancy-engine/sdk
ts
import { TenancyEngineClient } from '@tenancy-engine/sdk';

const te = new TenancyEngineClient({
  baseUrl: process.env.TENANCYENGINE_BASE_URL ?? 'https://auth.lab.saasruntime.com',
  apiKey: process.env.TENANCYENGINE_API_KEY!,
  appId: process.env.TENANCYENGINE_APP_ID!,
});

.NET

powershell
dotnet add package TenancyEngine.Sdk
csharp
using TenancyEngine.Sdk;

builder.Services.AddHttpClient("TenancyEngine", client =>
{
    client.BaseAddress = new Uri(builder.Configuration["TenancyEngine:BaseUrl"]
        ?? "https://auth.lab.saasruntime.com");
});

builder.Services.AddScoped(sp =>
{
    var httpFactory = sp.GetRequiredService<IHttpClientFactory>();
    var apiKey = sp.GetRequiredService<IConfiguration>()["TenancyEngine:ApiKey"]
        ?? throw new InvalidOperationException("TenancyEngine:ApiKey is required.");

    return new TenancyEngineClient(httpFactory.CreateClient("TenancyEngine"), apiKey);
});

4. Gate a request with runtime context

Runtime context is the one backend call your app should make before tenant-scoped work. It returns enabled features, limits, current usage, tenant status, and OIDC settings for the selected environment.

ts
const context = await te.getRuntimeContext('vectralabel', 'Development', tenantId);

if (!context.features.includes('labels.ai')) {
  throw new Response('Feature not enabled', { status: 403 });
}

const limit = context.limits['labels.monthly'];
const used = context.usage['labels.monthly'] ?? 0;

if (limit !== null && limit !== undefined && used >= limit) {
  throw new Response('Plan limit reached', { status: 402 });
}
csharp
var context = await te.GetRuntimeContextAsync("vectralabel", "Development", tenantId, ct);

if (context is null || !context.Features.Contains("labels.ai"))
{
    return Results.Forbid();
}

var limit = context.Limits.GetValueOrDefault("labels.monthly");
var used = context.Usage.GetValueOrDefault("labels.monthly");

if (limit is not null && used >= limit)
{
    return Results.Problem("Plan limit reached", statusCode: StatusCodes.Status402PaymentRequired);
}

5. Record usage with idempotency

Tie the idempotency key to your own operation ID so retries do not double-count usage.

ts
const result = await te.consumeUsage(
  'vectralabel',
  'demo-bakery',
  'Development',
  'labels.monthly',
  1,
  `label:${labelId}`,
);
csharp
var result = await te.ConsumeUsageAsync(
    "vectralabel",
    "demo-bakery",
    "Development",
    "labels.monthly",
    amount: 1,
    idempotencyKey: $"label:{labelId}",
    ct);

If the downstream operation fails after usage is consumed, call releaseUsage / ReleaseUsageAsync with a separate rollback idempotency key.

6. Verify webhooks

Create a webhook endpoint from Application -> Webhooks. Copy the signing secret when it is shown.

TenancyEngine sends X-TE-Signature: t={unixTimestamp},v1={hexHmac}. The HMAC input is {timestamp}.{rawBody}. Always verify the exact raw body bytes before parsing JSON.

ts
import { verifyWebhookSignature } from '@tenancy-engine/sdk';

const rawBody = await request.text();
const signature = request.headers.get('X-TE-Signature') ?? '';

if (!verifyWebhookSignature(rawBody, signature, process.env.TENANCYENGINE_WEBHOOK_SECRET!)) {
  throw new Response('Invalid signature', { status: 400 });
}
csharp
var rawBody = await new StreamReader(Request.Body).ReadToEndAsync();
var signature = Request.Headers["X-TE-Signature"].ToString();

if (!WebhookSignatureVerifier.Verify(rawBody, signature, webhookSecret))
{
    return Results.BadRequest("Invalid signature");
}

7. Use MCP for setup automation

The TenancyEngine MCP server exposes console/admin/runtime tools for AI agents and local automation.

powershell
cd D:\repos\tenancy-platform\mcp-server
npm ci
npm run build
$env:TE_BASE_URL = "https://auth.lab.saasruntime.com"
$env:TE_API_KEY = "<organization-api-key>"
npm start

Use TE_API_KEY for runtime tools such as get_runtime_context, consume_usage, and release_usage. Use an OIDC bearer token or local dev admin key for platform-admin tools.

TenancyEngine platform documentation