Joining the private beta
Perihelium is in private beta. Public sign-up is closed: a new organization exists only because Zynth invited you. This page is what an invited tester needs — what you receive, what to do first, and what the beta does and does not promise.
Private beta, AI-first operations. Perihelium is built and operated with AI agents in the loop and may change or be interrupted without notice. It carries no service level during the beta. Back up your own data; Zynth does not guarantee its retention during the beta. You acknowledged this when you applied; it applies to everything you connect.
Applying
Apply on the early-access page. Applications are reviewed in small groups and the ones selected receive an invitation by email. An application does not create an account, and an invitation is not guaranteed. To have your application deleted, email the address on that page.
What you receive
One beta invite code (zbi_…), issued to your email address, valid for a stated number of
days, in one of two modes. The email says which.
Bootstrap mode — your agent opens your organization
You connect through Zynth's own agent-native onboarding (agent-driven onboarding). Your MCP-capable agent calls the bootstrap with your email, your organization name, and the invite code; you — the human — verify the short code, prove your mailbox, set a passkey, review what the agent proposed, and approve. The agent then receives its own governed credential and you are the organization's owner.
The code is bound to your email and to this door: a different address, an expired or revoked code, or a code issued for the other mode is refused with one message, and the code is consumed the moment your organization is created — not when the bootstrap is parked.
curl -X POST "$BASE/api/v1/onboard/bootstrap" -H "Content-Type: application/json" \
-d '{"organization_name":"Acme Inc","human_email":"you@acme.example","invite_code":"zbi_…",
"client_info":{"name":"claude-code","platform":"cli","model":"claude-fable-5"}}'
# → 201 {"device_code":"…","user_code":"WXYZ-1234","verification_uri":"…/onboard/verify",
# "expires_in":3600,"interval":5,"pow_required":false}
Then poll for the credential
The bootstrap parks the request and hands back a device_code; the human legs (enter the
user_code, prove the mailbox, set a passkey, review the proposal, approve) run in the browser
at verification_uri. While that happens, your agent polls for the outcome at
POST /api/v1/onboard/poll with the device_code:
curl -X POST "$BASE/api/v1/onboard/poll" -H "Content-Type: application/json" \
-d '{"device_code":"…"}'
The poll answers one of:
| Answer | Meaning | What your loop does |
|---|---|---|
200 {"status":"authorization_pending","interval":5} | The human has not finished approving yet. | Wait interval seconds, then poll again. |
200 {"status":"approved",…,"agent":{…}} | Approved — this poll carries the credential (see below). | Store the credential and stop. |
200 {"status":"consumed","interval":5} | The credential was already released on an earlier poll. | Stop — never retry. |
200 {"status":"denied","interval":5} | The human declined the request. | Stop; ask for a new invite if this was a mistake. |
410 Gone | The bootstrap attempt expired (~1 hour — distinct from the emailed link's longer life). | Stop; start again with a fresh bootstrap. |
404 Not Found | Unknown device_code. | Stop. |
429 slow_down | You polled faster than interval. | Honour Retry-After / interval and slow down. |
Respect the interval back-off the bootstrap and every poll return (5 seconds by default):
polling faster earns 429 slow_down, not a faster answer.
The credential appears on exactly ONE poll, ever. The winning approved poll carries the
agent's own credential in a nested agent object; every poll after that returns consumed.
Your loop must terminate the moment it sees the credential (or consumed, denied, 410, or
404) — a loop that keeps polling forever after approved will only ever see consumed.
{
"status": "approved",
"interval": 5,
"agent": {
"agent_id": "…",
"name": "…",
"tenant_id": "…",
"credential": "agt_…"
}
}
tenant_id sits inside agent, so a freshly bootstrapped agent knows the organization it now
belongs to without a human opening the console. The credential is your agent's long-lived
secret, not an access token — there is deliberately no token_type on it. Store it where your
agent keeps secrets and exchange it for a short-lived access token at
POST /api/v1/agents/token, minting fresh tokens as they expire. Agent tokens are
DPoP-bound — send a DPoP proof with the
request, and the response's token_type (DPoP) tells you to present the token as
Authorization: DPoP <token> with a fresh proof on every call. A request without a proof is
refused 400 on a deployment that requires it, which is the default.
Provisioned mode — Zynth opened your organization for you
Zynth created your organization, a passwordless owner account for your email (you receive a set-password email — set a passkey right after), and an agent pre-registered with the authority you asked for. That agent cannot authenticate yet: the invite code is a claim code. Your agent presents it once and receives its first credential; nobody at Zynth ever sees it.
curl -X POST "$BASE/api/v1/agents/claim" -H "Content-Type: application/json" \
-d '{"claim_code":"zbi_…","owner_email":"you@acme.example"}'
# → {"agent_id":"…","tenant_id":"…","credential":"agt_…","token_type":"bearer"} (shown once)
A second claim is refused; a revoked or expired code is refused; a code issued for the other
mode is refused — all with the same message. Store the credential where your agent keeps
secrets and mint tokens at POST /api/v1/agents/token as usual.
Your organization waits for you before it can grow
An organization Zynth opened for you starts quarantined, and stays that way until you
sign in for the first time. Your agent can claim its credential, get tokens and read; what it
cannot do yet is grow the organization — invite members, add an auto-join domain, create API
keys or OAuth clients, add a webhook, connect an identity provider or SCIM, or apply a manifest.
Those answer 403 with the reason, and the reason names the remedy: sign in once.
This is the same restriction an organization opened by a bootstrapping agent sits under until its human approves — the principle is that no organization grows before a person has arrived in it. Signing in lifts it immediately and permanently; there is nothing else to click.
What to do first
- Sign in to the console at
https://auth.zynthmedia.comand add a passkey. In provisioned mode this is also what lifts your organization's quarantine, so do it before your agent starts configuring anything. - Review the agent under AI Agents: its governance band, its delegation, its kill switch.
- Connect what you told us you would test, and tell us what breaks.
Per-service audience isolation (OAuth resources)
Yes — a beta tenant can register OAuth resources for per-service audience isolation
(RFC 8707 resource indicators). If you run the OpenID Provider and want each of your APIs to
accept only tokens minted for it, register the API as an OAuth resource at
POST /api/v1/tenants/oauth-resources (needs tenant:manage, which a tenant owner holds).
It is not blocked by bootstrap quarantine, but register it once your tenant is out of
quarantine (email-confirmed / signed in for the first time), like the rest of the growth
surface.
Your client then names the resource with a resource= parameter on /authorize and /token,
and the issued access token's aud becomes that identifier — it verifies only at that API and is
rejected everywhere else. Resources are admin-curated: a client may name one, never create one.
The full walkthrough is in the RP guide's
audience-bound tokens section, and verifying such a
token at your own service is in Verifying tokens.
Where the API contract lives
The curated API reference is the contract. Build against it.
There is deliberately no spec to download: GET /openapi.json answers 404 and /docs
answers a 301 to docs.zynthmedia.com. Neither is an outage
and neither is a beta limitation waiting to lift — a generated spec would publish the surface as
it happens to be, including routes no customer can call, so the reference you read is curated on
purpose. If a 404 on /openapi.json sent you looking for a status page, this line is the
answer; if something you need is missing from the reference, tell us, because that is a defect in
the contract rather than a gap you should work around.
What the beta does not include
Purchases and plans (nothing is for sale), a service level, guaranteed data retention, and public sign-up. Everything the platform enforces is live: passkeys and MFA, the OpenID Provider, the MCP gateway, agent containment, the audit spine — and it is being tested on you as well.