Heimdall docs
Quickstart

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.

POST /api/v1/auth/signup
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.

POST /api/v1/apps
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.

invite flow
# 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:

POST /api/v1/invites/accept
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.

GET /api/v1/introspect/apps/:slug
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.

JWKS endpoint
GET https://api.heimdall.productcraft.co/<appSlug>/v1/.well-known/jwks.json

Each 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.

verify.ts
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;
}