Kizaki
ReferenceInspire

API Keys

Define route-access scopes in Inspire and manage actual tokens through the CLI.

The apiKeys block defines which machine-access scopes your app supports. Routes use those scope names through auth: apiKey(scope), and the platform handles the actual token lifecycle.

The key idea is that API keys should be explicit capabilities, not just long-lived passwords. A read key, an import key, and an admin key should mean different things in the schema.

Basic Shape

apiKeys {
  scopes: {
    read: @system("api_read") {
      grant read on Project
    },
    write: @system("api_write") {
      grant read, write on Project
    },
  }
}

Each scope names a machine capability. Routes can then require that capability directly.

routes("/v1", auth: apiKey(read)) {
  GET /projects -> listProjectsRoute
}

routes("/v1", auth: apiKey(write)) {
  POST /projects -> createProjectRoute
}

What The Block Does

The apiKeys block is not where you mint or rotate tokens. It defines:

  • which scope names exist
  • what each scope is allowed to do
  • which routes may use those scopes

Everything about token creation, listing, rotation, and revocation happens through the CLI or through server code using the SDK.

Scope Design

Good scope design is narrow and task-shaped.

apiKeys {
  scopes: {
    read: @system("api_read") {
      grant read on Project
      grant read on Task
    },
    importer: @system("api_importer") {
      grant read on Project
      grant read, write on Task
    },
    admin: @system("api_admin") {
      grant read, write, delete on Project
      grant read, write, delete on Task
    },
  }
}

In practice:

  • read is for reporting, sync, and external dashboards
  • task-shaped scopes like importer are for one integration workflow
  • admin should be rare

If one key can do everything, you lose most of the benefit of declaring scopes at all.

Lifecycle: CLI

Use the CLI when you need to create or rotate a key directly.

kizaki api-keys create --scope read --name "Mobile app"
kizaki api-keys create --scope importer --name "CRM sync" --expires 90d
kizaki api-keys list
kizaki api-keys list --scope read
kizaki api-keys revoke <id>
kizaki api-keys rotate <id> --grace-period 24h

Two details matter:

  • the plaintext token is shown only on create and rotate
  • rotate can keep the old token alive briefly so integrations have time to switch

Lifecycle: Server Code

If your app lets customers create their own integration keys, do that through normal server functions.

import { getContext } from "@kizaki/sdk";

/** @expose */
export async function createReportingKey(name: string) {
  const { sdk } = getContext();

  return sdk.apiKeys.create({
    scope: "read",
    name,
    expiresIn: 60 * 60 * 24 * 90,
    metadata: { createdFrom: "settings" },
  });
}

/** @expose */
export async function rotateReportingKey(keyId: string) {
  const { sdk } = getContext();

  return sdk.apiKeys.rotate(keyId, {
    gracePeriod: 60 * 60 * 24,
  });
}

/** @expose */
export async function listIntegrationKeys() {
  const { sdk } = getContext();
  return sdk.apiKeys.list();
}

That keeps key-management permissions inside the same application workflows you already expose to your own UI.

What Callers Get Back

On create or rotate, the returned object includes the plaintext token once.

const key = await sdk.apiKeys.create({
  scope: "read",
  name: "Data warehouse sync",
});

key.id;
key.scope;
key.name;
key.token;      // plaintext, shown once
key.createdAt;
key.expiresAt;

On list, callers only get metadata.

const keys = await sdk.apiKeys.list({ scope: "read" });

// Each item includes metadata only.
// No token values are returned from list.

That is the right security posture for machine credentials: show the secret once, then treat it as write-only.

API Keys Vs kizaki keys

These are different systems.

  • api-keys are app-level credentials for your declared HTTP routes
  • keys are platform or CI credentials for working with Kizaki itself

If the credential is meant to call your app's routes, it belongs in apiKeys, not in kizaki keys.

  • keep scopes narrow and named after real capabilities
  • prefer several small scopes over one broad integration key
  • treat admin-style keys as exceptional
  • rotate keys with a grace window when integrations cannot switch instantly
  • manage customer-facing keys through your own exposed functions

Related docs:

On this page