Install the SDK
Apps built from a template already have the SDK wired up. Covers using it right away, plus install and auth when you wire it yourself.
The SDK is how your app code calls AxHub features (the signed-in user, company data).
Apps built from a template already have it, so there's nothing to install — start from using it right away.
If you built from a template
Template apps start with all of this already in place.
| Already set up | What it means |
|---|---|
| SDK install | @ax-hub/sdk is already a dependency (Next.js, Astro) |
| Connection settings | The API address, app slug, and company slug are filled into the code at deploy time |
| Auth | No token to issue — it uses the login of whoever is visiting the app |
Anyone reaching your app is already signed in with their company account. The app hands that login straight to the SDK, so there's never an API key for you to create and paste into code.
Using it right away
Call makeAxhub() from lib/axhub-server.ts and you get an SDK scoped to whoever made the current request.
import { makeAxhub } from '@/lib/axhub-server';
const sdk = await makeAxhub();
const me = await sdk.identity.me();
// me.email, me.name, me.tenants (companies and roles)import { makeAxhub } from '../lib/axhub-server';
const sdk = makeAxhub({ cookie: Astro.request.headers.get('cookie') });
const me = await sdk.identity.me();Call makeAxhub() fresh on every request. Stashing one outside the module and reusing it mixes up whose credentials a call was made with.
If it's a UI-only app (Vite + React)
That template runs only in the browser, so it doesn't use the SDK. Instead, axhubFetch() in lib/axhub.ts carries the login along.
import { axhubFetch } from './lib/axhub';
const res = await axhubFetch('/api/v1/me');
const me = await res.json();If the login has expired (401), it sends the visitor to AxHub login and brings them back to your app.
If you built without a template
Even without a template, you don't need to issue a token. The login arrives with the request to your app's address — pull it out and hand it to the SDK. That's all the templates do.
import { AxHubClient } from '@ax-hub/sdk';
// Build a fresh one per request.
const token = req.cookies['_hub_access'] ?? '';
const sdk = new AxHubClient({
baseUrl: process.env.AXHUB_API_URL,
...(token ? { token, tokenType: 'jwt' as const } : {}),
defaultTenantSlug: '<your company slug>',
});A deployed app already has these environment variables. You don't set them yourself.
| Environment variable | When it arrives |
|---|---|
AXHUB_API_URL | Every app — the AxHub API address |
AXHUB_APP_TOKEN | Every app — the key for sending notifications and mail only |
DATABASE_URL · DIRECT_DATABASE_URL | Apps with a database turned on |
STORAGE_ENDPOINT · STORAGE_BUCKET · STORAGE_ACCESS_KEY · STORAGE_SECRET_KEY | Apps with storage turned on |
AXHUB_APP_TOKEN is for sending notifications and mail only. Put it in an SDK client and it won't work.
If you're calling from outside the app
In a CI script or a batch job — somewhere with no signed-in person — there's no login to pass along. That's the one case where you issue a token.
Install
npm install @ax-hub/sdkThe six SDKs differ only in naming and syntax — they call the same backend the same way.
Issue a token
Issue a PAT (personal API key) while signed in as yourself.
curl -X POST "$AXHUB_API_URL/api/v1/me/personal-access-tokens" \
-H "Authorization: Bearer <your login token>" \
-H "Content-Type: application/json" \
-d '{ "name": "ci-script", "expires_in_days": 90 }'The issue response shows it exactly once. Copy it right then — you can't see it again, only issue a new one. Omit expires_in_days and it never expires.
A PAT acts with your own account's permissions — it can't do anything you couldn't. Listing and revoking are in SDK API — data.
Authenticate
Build a client with the token you issued.
import { AxHubClient } from '@ax-hub/sdk';
const sdk = new AxHubClient({
token: process.env.AXHUB_TOKEN!,
tokenType: 'pat', // 'pat' | 'jwt'
});- Always state the token type (
tokenType) — the SDK never guesses. - The SDK handles the headers: PAT goes out as
X-Api-Key, JWT asAuthorization: Bearer. - Don't write the token value in code; keep it in an environment variable — see Set environment variables for how.
Use a PAT on the server only. Put it in browser-side code and every visitor walks away with your account's permissions.
When something goes wrong
The SDK tells you what happened via e.code. Branch on code, never on the message text — wording can change later.
import { AxHubError, ConflictError } from '@ax-hub/sdk';
try {
await sdk.identity.me();
} catch (e) {
if (e instanceof ConflictError) {
// handle the already-done (duplicate) case
} else if (e instanceof AxHubError) {
console.error(e.code, e.requestId);
}
}These come up most often.
| Situation | What to do |
|---|---|
AxHubClient requires tokenType | Happens when you build new AxHubClient({ token }) yourself. In a template app, use makeAxhub() — it fills the type in |
TenantSlugRequiredError | Happens when calling sdk.apps.* directly. Go through makeTenant() and the company slug is attached for you |
401 | The login expired, or the token is wrong |
You're done when
sdk.identity.me() comes back with your email and your company — that means you're set up.
Next, turn on an app-scoped DB in Connect a database.