Quickstart.
Heimdall in five steps.
By the end of this guide you’ll have an account, an App, roles, and a working token verification flow. Plan ten minutes.
Prerequisites
- Any HTTP client (curl, fetch, Postman, etc.)
Step 1
Create an account
Sign up for a Heimdall account. This gives you an identity that can create and manage Apps.
curl -X POST https://heimdall.productcraft.co/api/v1/auth/signup \
-H "Content-Type: application/json" \
-d '{
"email": "you@example.com",
"username": "yourname",
"password": "YourSecurePassword123"
}'Response
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}Save the access_token — you'll use it in the next steps.
Step 2
Create an App
An App represents a tenant in your system. Every role, permission, and invite lives within a specific App. Create one for your first customer or your development environment — you become the owner automatically.
curl -X POST https://heimdall.productcraft.co/api/v1/apps \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"slug": "acme-corp",
"displayName": "Acme Corporation"
}'Response
{
"id": "550e8400-e29b-41d4-a716-...",
"slug": "acme-corp",
"display_name": "Acme Corporation",
"status": "active",
"metadata": {},
"created_at": "2026-02-20T10:00:00Z"
}Three system roles are created automatically: owner, admin, and member.
Step 3
Invite a team member
Create an invite to bring another user into your App with a specific role. First, list your App’s roles to find the role name you want to assign.
# List roles to find the admin role name
curl https://heimdall.productcraft.co/api/v1/apps/acme-corp/roles \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"
# Create an invite
curl -X POST https://heimdall.productcraft.co/api/v1/apps/acme-corp/invites \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"role": "admin",
"email": "colleague@example.com",
"maxUses": 1,
"expiresInHours": 72
}'The response includes a code field. Send this code to your colleague. They sign up, then accept the invite:
curl -X POST https://heimdall.productcraft.co/api/v1/invites/accept \
-H "Authorization: Bearer COLLEAGUE_ACCESS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "code": "INVITE_CODE" }'Step 4
Check permissions
Use the introspection endpoint to see what permissions a user has within a specific App.
curl https://heimdall.productcraft.co/api/v1/introspect/apps/acme-corp \
-H "Authorization: Bearer YOUR_ACCESS_TOKEN"Response
{
"isMember": true,
"role": { "id": "...", "name": "owner" },
"permissions": [
"user.create",
"user.read",
"user.update",
"user.delete",
"user.list",
"role.create",
"role.read",
"..."
]
}Step 5
Verify tokens locally
For production use, verify tokens locally using the public JWKS endpoint. Fetch the keys once, cache them, and verify tokens without a network round-trip on every request.
GET https://api.heimdall.productcraft.co/<appSlug>/v1/.well-known/jwks.jsonEach app has its own signing key. Pin the JWKS URL and the issuer to YOUR app — a token minted for another app on the platform cannot pass.
Use any JWT library in your language of choice (jose, jsonwebtoken, PyJWT, etc.) to verify the token signature against these keys.
import * as jose from 'jose';
const APP_SLUG = 'acme';
const JWKS = jose.createRemoteJWKSet(
new URL(`https://api.heimdall.productcraft.co/${APP_SLUG}/v1/.well-known/jwks.json`)
);
async function verifyToken(token) {
const { payload } = await jose.jwtVerify(token, JWKS, {
issuer: `https://api.heimdall.productcraft.co/${APP_SLUG}`,
audience: APP_SLUG,
});
return payload;
}