Hanzo AI

Integrations

How your org connects third-party accounts like Slack, and revokes them.

Also for this capability: API · CLI · SDKs

How your org connects third-party accounts like Slack, and revokes them.

Base URLhttps://api.hanzo.ai
Operations50
AuthAuthorization: Bearer $HANZO_API_KEY

Specification

HIP-1250 · Integrations — The Connection Registry — Draft · read the specification →

An integration is a connection to a provider the platform does not own — Slack, GitHub, Google, Stripe — held on behalf of a tenant, with the credential in custody and the connection state in one registry. hanzoai/cloud apps/integrations is that registry, and /v1/integrations is its address.

The capability has two audiences over the one store: the org plane, where an admin connects a provider for the whole org, and the user plane, where a person links their own accounts. HIP-0126 fixed the vocabulary and HIP-1065 specifies the user plane's custody rules; this HIP is the capability declaration — the store, the target address, the operations, and what the surface refuses.

Motivation

Provider connections were the platform's most duplicated concern: each consumer that needed a token — automations, channels, marketing — was one bad refactor away from its own OAuth path and its own secret row. One registry with one custody exit is what makes "is this org connected to X" a fact askable in one place, and a token something no peer ever stores.

Specification

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be interpreted as in RFC 2119.

§1 The store

The capability owns one store: the integrations SQLite database (apps/integrations/store.go:73, sqlpool.Open("integrations", dir)). One row per (org, provider, owner); tenancy is the org column, which is part of the primary key. Rows hold non-secret state only. Credentials live solely in the key store (HIP-0027), sealed under a path built from the tenant — org connections under the org, user connectors under the (org, user) pair (apps/integrations/integrations.go:1564). A row MUST NOT carry a secret and a custody failure MUST NOT leave a row behind.

§2 The address

Every route answers under /v1/integrations: the provider lifecycle (/{provider}, /{provider}/connect, /callback, /verify, /disconnect), the provider-specific doors (Slack, GitHub, Discord, Teams, Telegram, GitLab, OpenRouter), and the user plane at /v1/integrations/connectors. Today the user plane is served at a second root, /v1/connectors; that pair is carried by cloud's openapi/misfiled.txt:34 and closes by fold — one store, so per HIP-0139 §7.1 there is no boundary to split on. The generated clients re-point on regeneration; the installed CLI's interactive device and PKCE flows (cli/src/commands/product/generated.rs:794) hard-code the old root and a flow in flight when the fold lands polls a dead path, so the fold MUST ship in a CLI release that re-points them.

§3 Operations, typed and declared

The connectors plane is typed end to end (apps/integrations/connectors.go:55): list, providers, token read, device start/poll, credential intake, refresh, delete. The org plane's OAuth callbacks, link flows and provider webhooks are declared with prose beside the route (openapi.Describe, apps/integrations/integrations.go) because none can be a value: a callback answers a redirect the provider dictates, a link flow renders HTML, and a webhook's authentication is the provider's signature over the raw body — the signature check IS the authentication and it fails closed (apps/integrations/integrations.go:602).

§4 Tenancy

A request becomes a tenant through the validated principal's org (HIP-0026, principal.Org); a client-forged org header is refused. Org-plane writes that change what the whole org is connected to require the caller's own-org admin bit (principal.IsOrgAdmin — never SuperAdmin, apps/integrations/integrations.go:1096). User-plane rows are keyed by the (org, user) pair per HIP-1065, with no admin gate. Provider webhooks arrive with no principal and are admitted by signature alone.

§5 Metering, events, telemetry, stage

The capability is free, said in those words: plugin/integrations/main.go:29 declares Price: cloud.Free and no spend table names it. It publishes no events on the bus. Peers do not read its store: whether an org is connected is asked over the internal plane (apps/integrations/connection_rpc.go:30), and token handoff to in-process consumers goes through the same seam, never the address. It emits nothing to observability beyond the request span every route gets. Stage: ga.

§6 Upstream

The capability derives from none. Every provider client is hand-rolled Go over net/http against the provider's public API; no vendor SDK is imported and no third-party project is forked, embedded or mirrored.

Rationale

The alternative to one registry with two planes is two capabilities — org integrations and user connectors — which reads well until the store is drawn: both would open the same database, the defect HIP-0106 names. The two audiences differ in key, not in kind, so they are one capability whose address says which plane a route is on.

Security Considerations

The wrong implementation hands an attacker credentials. The exposures, each closed in code: a secret in a row or log (custody is the only holder, one operation returns a token); a cross-tenant read (org in the primary key, the (org, user) pair as the user-plane row key); a forged provider webhook (signature verification, fail closed); a concurrent refresh destroying the credential it refreshes (single-flight with adoption, apps/integrations/refresh.go:20); and custody-path smuggling (the path is validated before use).

Four surfaces

SurfaceReaches this capability asCoverage
RESTintegrations at its own prefix50 operations
CLIhanzo integrations …50 of 50
SDKIntegrationsApi in every published client50 methods
MCPtool integrations on https://api.hanzo.ai/v1/mcp45 operations, 0 under the document's own id — ask describe for the rest

Quickstart

export HANZO_API_KEY=sk-...   # console.hanzo.ai → API keys

Then the first call — a read that needs nothing but the key. GET /v1/integrations, operation get_integrations:

hanzo integrations list

Answers 200 with object — ok.

Endpoints

EndpointWhat it does
GET /v1/integrations/{provider}/callbackOAuth return for any connector
POST /v1/integrations/{provider}/connectAcquires the org's credential for one provider.
POST /v1/integrations/{provider}/disconnectRevokes (best-effort) and forgets an org's connection: it deletes every custodied KMS secret and the connection row.
POST /v1/integrations/{provider}/verifyRe-checks a CONNECTED apikey connector's stored credential against the provider, live (hanzo connector verify).
GET /v1/integrations/{provider}Returns ONE provider with this org's connection status — the same view list carries, for a single id.
POST /v1/integrations/connectors/{id}/refreshForces a token rotation for a connected connector, ahead of the automatic rotation a token read would do inside the expiry window.
GET /v1/integrations/connectors/{id}/tokenHands the custodied access token to its owner — the ONE place custody exits.
DELETE /v1/integrations/connectors/{id}Forgets a connector: every custodied secret, then the row.
POST /v1/integrations/connectors/{provider}/credentialIs the direct intake path: a customer-held token/setup-token (Verify) or an externally obtained OAuth bundle from the CLI's local PKCE (Adopt).
POST /v1/integrations/connectors/{provider}/device/{flow}/pollAdvances a device sign-in.
POST /v1/integrations/connectors/{provider}/deviceBegins a device sign-in and returns the code to show the user plus how to poll for completion.
GET /v1/integrations/connectors/providersLists the user-scoped provider cards — the catalog of what a user can connect, and how.
GET /v1/integrations/connectorsLists the caller's OWN connectors across every provider — the set hanzo connector ls prints.
POST /v1/integrations/discord/interactionsDiscord interactions endpoint
GET /v1/integrations/discord/link/callbackComplete the Discord account link
GET /v1/integrations/discord/link/discordDiscord sign-in return leg
GET /v1/integrations/discord/linkBegin linking a Hanzo account from Discord
POST /v1/integrations/github/claimBinds installations the App ALREADY holds to the org the caller is acting in — the reconciliation for a grant that happened outside our connect flow.
POST /v1/integrations/github/forkForks a granted repository.
GET /v1/integrations/github/installationsLists the GitHub accounts the caller may see the App installed on, each confirmed against the App's own list, plus where to add another.
POST /v1/integrations/github/issues/backfillSeeds the native todo with the EXISTING issues across the org's granted repos (default state=open); the webhook keeps them live thereafter.
POST /v1/integrations/github/repos/{repo}/pages/buildsRequests a Pages rebuild and returns the queued build's status.
GET /v1/integrations/github/repos/{repo}/pagesReturns the repo's Pages status, live URL, custom domain and build source.
POST /v1/integrations/github/repos/{repo}/pagesCreates the repo's Pages site and answers 201 Created with it.
PUT /v1/integrations/github/repos/{repo}/pagesSets or clears the custom domain (cname) and updates HTTPS enforcement, build type, or source.
DELETE /v1/integrations/github/repos/{repo}/pagesDeletes the repo's Pages site.
POST /v1/integrations/github/repos/importImports the selected (or all) granted repos into git.hanzo.ai.
GET /v1/integrations/github/reposLists the org's granted GitHub repositories, each annotated with its native import + sync status from the git object plane.
POST /v1/integrations/github/searchFinds repositories on GitHub.
POST /v1/integrations/github/webhookGitHub App webhook
GET /v1/integrations/gitlab/projectsLists the projects the org's GitLab connection can reach — membership projects, most recently active first.
POST /v1/integrations/openrouter/webhookReceive OpenRouter Broadcast traces as usage rows
POST /v1/integrations/slack/commandsSlack slash command webhook
POST /v1/integrations/slack/eventsSlack Events API webhook
GET /v1/integrations/slack/installInstall the Hanzo app into a Slack workspace
GET /v1/integrations/slack/link/callbackComplete the Slack account link
GET /v1/integrations/slack/link/slackSlack sign-in return leg
GET /v1/integrations/slack/linkBegin linking a Hanzo account from Slack
POST /v1/integrations/teams/eventsMicrosoft Teams Bot Framework webhook
GET /v1/integrations/teams/link/aadMicrosoft sign-in return leg
GET /v1/integrations/teams/link/callbackComplete the Teams account link
GET /v1/integrations/teams/linkBegin linking a Hanzo account from Teams
POST /v1/integrations/telegram/connectMints a short, single-use deep-link code bound to the caller's org and returns the t.me link the console navigates to.
GET /v1/integrations/telegram/link/authTelegram Login Widget return leg
GET /v1/integrations/telegram/link/callbackComplete the Telegram account link
GET /v1/integrations/telegram/linkBegin linking a Hanzo account from Telegram
POST /v1/integrations/telegram/webhookTelegram Bot API webhook
GET /v1/integrations/whatsapp/webhookWhatsApp Cloud API subscription challenge
POST /v1/integrations/whatsapp/webhookWhatsApp Cloud API webhook
GET /v1/integrationsReturns every registered integration provider together with THIS org's connection status for it — the catalog the console's Integrations page renders.

All Hanzo APIs · Interactive reference

How is this guide?