Connect with Helipod (OAuth)
Let people connect your app to their Helipod account. Register an app, ask for access with OAuth 2.1 and PKCE, and call the Helipod API on their behalf.
"Connect with Helipod" lets your app work in someone else's Helipod account: deploy their repositories, restart services, manage their VPS, register domains or send email, with the access they approve. Nobody copies an API key. The account owner chooses the products, read-only or full access, whether your app may spend from their wallet, and a monthly limit. They can change or remove it any time in Settings → API keys → Connected apps.
It is standard OAuth 2.1 with PKCE, the same sign-in the AI tools use. A token works on the whole public API: the Container, Database, VPS, Domain, Email, Account and AI APIs, the AI Gateway and the hosted MCP server.
Register your app
Open Developer apps
In the dashboard, open any project and go to Settings → Developer apps, then Register app. Apps belong to your account, not to the project.
Fill in the details
- App name and Homepage (https). Names cannot contain "Helipod".
- What it does: one sentence, shown to users once the app is verified.
- Logo URL (https, optional, square), shown once verified.
- Redirect URIs, one per line:
https://…,http://localhost…for apps on the user's computer, or your app's own scheme (myapp://callback). - It has a server that can keep a secret: tick it for a web app with a backend. Leave it off for a single-page app, a mobile app or a CLI: those use PKCE only, without a secret.
Copy the credentials
You get a Client ID (hoa_…) and, for server apps, a Client secret (hel_ocs_…) shown once. Keep the secret on your server. New secret replaces it; the old one stops working at once.
An app that is not reviewed works, but users see "Not reviewed by Helipod" on the consent screen. Click Request verification: we check the homepage, the name, the redirect URIs and what the app does. Verified apps show their name, logo and description, get a verified badge, and are listed on helipod.io/apps. Changing the name, logo, homepage or redirect URIs of a verified app sends it back for review.
The flow
| Authorization endpoint | https://helipod.io/oauth/authorize |
| Token endpoint | https://api.helipod.io/oauth/token |
| Revocation endpoint | https://api.helipod.io/oauth/revoke |
| Metadata | https://api.helipod.io/.well-known/oauth-authorization-server |
| PKCE | required, S256 |
| Access token | hel_oat_…, 1 hour |
| Refresh token | hel_ort_…, 60 days, replaced on every use |
Send the user to Helipod
Create a random code_verifier, its code_challenge (BASE64URL of its SHA-256) and a state, then redirect:
https://helipod.io/oauth/authorize
?response_type=code
&client_id=hoa_your_client_id
&redirect_uri=https://example.com/auth/helipod/callback
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&scope=containers vps:read spend
&state=af0ifjsldkjThe user signs in (Google), sees what you ask for, can narrow it, and clicks Allow.
Exchange the code
Helipod redirects to your redirect_uri with code and your state. Check the state, then:
curl https://api.helipod.io/oauth/token \
-u "hoa_your_client_id:hel_ocs_your_secret" \
-d grant_type=authorization_code \
-d code=THE_CODE \
-d redirect_uri=https://example.com/auth/helipod/callback \
-d code_verifier=THE_VERIFIERA public app (no secret) sends client_id in the body instead of -u. The answer:
{
"access_token": "hel_oat_…",
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": "hel_ort_…",
"scope": "containers vps:read spend"
}scope is what the user actually allowed, which can be less than you asked for.
Call the API
Send the access token like an API key:
curl https://api.helipod.io/api/containers/projects \
-H "Authorization: Bearer hel_oat_…"When it expires, get a new pair with grant_type=refresh_token&refresh_token=hel_ort_…. Every refresh returns a new refresh token: store it and drop the old one. Using an old refresh token again ends the whole connection, because it means it leaked.
Scopes
| Scope | Gives |
|---|---|
containers, databases, vps, domains, email, ai, account | Full access to that product |
containers:read, vps:read, … | Read only: GET requests, price checks (?dryRun=true) and read-only SQL |
spend | May charge the wallet: create or upgrade services, VPS, domains, plans, renewals, AI top-ups |
The user decides in the end: they can switch a product to read only, turn purchases off, set a monthly limit, or reach only one project. Without a whole-account connection, the Domain API and the account's money routes are not available, as with API keys.
Purchases and the monthly limit
Without spend, every request that would cost money answers 403 PURCHASE_NOT_ALLOWED. The same request with ?dryRun=true still works, so your app can show the price and ask the user to allow purchases.
With a monthly limit, each purchase is priced before it runs. If it would go over the limit it is refused with 402 SPEND_LIMIT_REACHED, and nothing is charged. A purchase whose price cannot be checked first is refused with 402 SPEND_LIMIT rather than run unchecked. The limit counts what your app bought this calendar month; the daily cost of services that keep running is not counted. AI Gateway usage through your app has its own monthly counter with the same limit.
Errors
Besides the usual API errors (see Requests and errors):
| Status | code | Meaning |
|---|---|---|
| 401 | INVALID_TOKEN | The access token expired or was revoked: refresh it, or sign in again |
| 403 | SCOPE_NOT_ALLOWED | The user did not give your app this product |
| 403 | READ_ONLY_ACCESS | Your app may only read this product |
| 403 | PURCHASE_NOT_ALLOWED | Your app may not spend from the wallet |
| 402 | SPEND_LIMIT_REACHED / SPEND_LIMIT | Over the monthly limit, or its price could not be checked first |
| 429 | APP_RATE_LIMITED | Your app sent too many requests in the last minute, across all its users |
If Helipod turns an app off (abuse, a security problem), its tokens stop working at once and the token endpoint answers invalid_client. The developer sees why in Developer apps.
AI Gateway with the user's balance
With ai (full access) and spend, your app can call the AI Gateway with the user's access token instead of an AI key, and the usage is paid from their AI balance:
import OpenAI from "openai"
const client = new OpenAI({ baseURL: "https://ai.helipod.io/v1", apiKey: userAccessToken }) // hel_oat_…
const r = await client.chat.completions.create({ model: "glm-5.3", messages: [{ role: "user", content: "Halo!" }] })The user sees your app as an AI key named "App: your app name" in AI Gateway → API keys, with its usage, and can revoke it there.
Deploy on Helipod button
For an open-source project you do not need OAuth at all: add a button that deploys your repository into the visitor's own account.
[](https://helipod.io/deploy?repo=owner/name&port=3000)| Parameter | |
|---|---|
repo | owner/name on GitHub, or an https URL of any public Git host (required) |
branch | Branch to build (default: the repository's default branch) |
root | Folder of the app in a monorepo |
port | Port the app listens on: it gets a public *.helipod.app domain |
name | Service name (letters, digits, - and _) |
The visitor signs in, picks an environment and clicks Deploy. Helipod builds the repository with the usual build detection. For several services, variables or a database, publish a template instead: its page has the same button.