Kizaki
ReferenceInspire

Integrations

Declare managed per-user OAuth integrations separately from auth.

The integrations block declares delegated external access for your app, separate from auth.

  • auth answers: who is the user?
  • integrations answers: which external APIs has this user connected?

Logging in with Google does not grant Google Calendar access. If your app needs Calendar, Drive, or another provider API, declare it in integrations.

Current Scope

Shipped:

  • Managed per-user OAuth2 connection lifecycle
  • Platform-owned connect, callback, refresh, disconnect, reconnect-required state, and audit trail
  • Browser helpers for connection status and connect/disconnect flows
  • Server helpers for short-lived provider access tokens
  • Typed Google Calendar server helpers

Not yet shipped:

  • Browser access to provider tokens
  • Workspace-owned/shared integrations
  • Marketplace-installed providers

Syntax

integrations {
  calendar: googleCalendar {
    mode: optional,
    onboarding: afterAuth,
    access: read,
    clientId: @secret("GOOGLE_CLIENT_ID"),
    clientSecret: @secret("GOOGLE_CLIENT_SECRET"),
  },

  notion: customOauth2 {
    mode: manual,
    onboarding: settings,
    access: read,
    clientId: @secret("NOTION_CLIENT_ID"),
    clientSecret: @secret("NOTION_CLIENT_SECRET"),
    authorizeUrl: "https://api.notion.com/v1/oauth/authorize",
    tokenUrl: "https://api.notion.com/v1/oauth/token",
    accountUrl: "https://api.notion.com/v1/users/me",
    accountIdPath: "bot.owner.user.id",
    accountEmailPath: "bot.owner.user.person.email",
    accountNamePath: "name",
    scopes: ["read:pages"],
    upgradeScopes: ["read:pages", "update:pages"],
    revokeUrl: "https://api.notion.com/v1/oauth/revoke",
  },
}

Fields

FieldMeaning
moderequired, optional, or manual
onboardingafterAuth, combined, or settings
accessread or write
clientId / clientSecretProvider OAuth client credentials via @secret(...)
scopesRequested scopes for the initial connection
upgradeScopesScopes requested when the app upgrades access
authorizeUrl / tokenUrl / accountUrlRequired for customOauth2 providers
accountIdPath / accountEmailPath / accountNamePathJSON paths for provider account metadata
revokeUrlOptional provider revoke endpoint

Modes

ModeMeaning
requiredThe integration must be connected as part of setup
optionalThe app works without it but benefits from it
manualUsers connect from settings or a later workflow

Onboarding Hints

SettingMeaning
afterAuthPrompt after login or signup
combinedPresent as one onboarding sequence (auth and integration remain separate underneath)
settingsOnly prompt from settings or a feature gate

These are product hints. They do not merge auth and integrations into one backend record.

Provider Types

customOauth2

Use customOauth2 when Kizaki owns the OAuth lifecycle but your app owns provider-specific API calls and sync logic.

Required fields:

  • clientId
  • clientSecret
  • authorizeUrl
  • tokenUrl
  • accountUrl
  • accountIdPath

Security:

  • Provider endpoints must use https://, except localhost-only testing URLs
  • The compiler rejects insecure plaintext provider URLs
  • The runtime rejects provider URLs that resolve to loopback, private, or link-local addresses
  • Token and account responses use a bounded JSON client; oversized responses are rejected
  • Provider error bodies are not reflected into platform logs
  • In local dev, integration secrets are read from KIZAKI_DEV_SECRET_<NAME> or the platform secret store

googleCalendar

Built-in provider declaration with:

  • Google-specific OAuth defaults
  • Read/write access-tier modeling
  • Managed connection lifecycle on the same substrate as customOauth2
  • Typed server helpers in @kizaki/sdk

Browser code gets lifecycle helpers only. Typed Calendar operations are server-only.

Browser SDK

import {
  listIntegrations,
  getIntegrationStatus,
  beginIntegrationConnect,
  beginIntegrationUpgrade,
  disconnectIntegration,
} from "@kizaki/sdk/browser";

These helpers talk to /__integrations/* endpoints. They return status and redirect the browser into the provider consent flow. They do not expose raw provider tokens.

Server SDK

import {
  getIntegrationStatus,
  getIntegrationAccessToken,
  withIntegrationAccess,
} from "@kizaki/sdk";

Server-only. Returns short-lived access tokens for the current authenticated user. Never exposes refresh tokens.

For Google Calendar, use the typed helper:

import { googleCalendar } from "@kizaki/google";

/** @expose */
export async function nextWeekBusyWindows() {
  return googleCalendar("calendar").getFreeBusy({
    timeMin: "2026-06-01T00:00:00Z",
    timeMax: "2026-06-08T00:00:00Z",
    calendarIds: ["primary"],
  });
}

Available methods:

  • getStatus()
  • listCalendars()
  • getFreeBusy(...)
  • listEvents(...)
  • createEvent(...)
  • updateEvent(...)
  • cancelEvent(...)

Raw DIY Integrations

The managed substrate is not the only option. You can build integrations directly with:

  • routes(..., auth: none) for webhooks
  • req.rawBody for signature verification
  • @secret(...) for API keys or provider credentials
  • network {} for outbound allowlists
  • Effects and schedules for retry and sync work

Use the raw path for webhook-only, API-key, or partner-specific integrations where delegated per-user OAuth is unnecessary.

  • Use auth for identity
  • Use integrations for delegated provider access
  • Use customOauth2 when Kizaki should own token custody and refresh
  • Use raw routes/network/secrets for simple integrations without delegated OAuth
  • Keep provider API calls in server code

Related:

On this page