Auth docs
Quickstart

Quickstart.

Auth in five steps.

By the end of this guide you'll have an App, a signed-in end-user, and a working token verification flow. Plan ten minutes.


Prerequisites

  • A ProductCraft account and workspace with the Auth service enabled — sign up at auth.productcraft.co if you don't have one.
  • Any HTTP client (curl, fetch, Postman, etc.)

Step 1

Get a platform token

Apps are managed with your ProductCraft workspace credentials — not with end-user tokens. Exchange your account email + password for a platform access token:

POST /v1/auth/signin
curl -X POST https://api.platform-auth.productcraft.co/v1/auth/signin \
  -H "Content-Type: application/json" \
  -d '{
    "identifier": "you@example.com",
    "password": "YourAccountPassword"
  }'

Response

{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600
}

Save the access_token — it's the <platform-access-token> used in steps 2 and 3. For long-lived scripts and CI, create a workspace API key instead (console → Workspace → API keys); a pcft_live_… key works everywhere <platform-access-token> appears below.

You'll also need your workspace id for step 2 — grab it from the same API:

GET /v1/workspaces
curl https://api.platform-auth.productcraft.co/v1/workspaces \
  -H "Authorization: Bearer <platform-access-token>"

Step 2

Create an App

An App is one pool of end-users with its own roles, permissions, and signing keys. Create one for your product (or your development environment). It belongs to your workspace.

POST /v1/apps
curl -X POST https://api.auth.productcraft.co/v1/apps \
  -H "Authorization: Bearer <platform-access-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "acme-corp",
    "display_name": "Acme Corporation",
    "workspace_id": "<workspace-id>"
  }'

Response (excerpt)

{
  "id": "550e8400-e29b-41d4-a716-...",
  "slug": "acme-corp",
  "display_name": "Acme Corporation",
  "workspace_id": "<workspace-id>",
  "status": "active",
  "metadata": { "auth": { "signup_enabled": false, "...": "..." } },
  "created_at": "2026-07-27T10:00:00.000Z",
  "updated_at": "2026-07-27T10:00:00.000Z"
}

Three system roles are created automatically: owner, admin, and member.


Step 3

Invite your first end-user

Self-serve signup is disabled on new Apps by default, so mint an invite. Auth emails the code to the recipient — and also returns it in the response, which is all you need here.

POST /v1/apps/acme-corp/end-users/invites
curl -X POST https://api.auth.productcraft.co/v1/apps/acme-corp/end-users/invites \
  -H "Authorization: Bearer <platform-access-token>" \
  -H "Content-Type: application/json" \
  -d '{
    "email": "newuser@example.com",
    "name": "New User",
    "role_name": "member"
  }'

Response (excerpt)

{
  "id": "81361444-...",
  "code": "inv_...",
  "email": "newuser@example.com",
  "role_name": "member",
  "expires_at": "2026-07-30T10:00:00.000Z"
}

The recipient accepts by POSTing the code back with a password. The default password policy requires 12+ characters with an uppercase letter and a number:

POST /:appSlug/v1/auth/accept-invite
curl -X POST https://api.auth.productcraft.co/acme-corp/v1/auth/accept-invite \
  -H "Content-Type: application/json" \
  -d '{ "code": "inv_...", "password": "Correct-Horse-42" }'

The response is the same shape as a sign-in: an access + refresh token pair scoped to the App. Save this access_token as <end-user-access-token> for step 4. (Workspace teammates who manage the App itself are not end-users — invite them in the console instead.)


Step 4

Check permissions

Ask the Consumer API what the signed-in end-user is allowed to do:

GET /:appSlug/v1/me/permissions
curl https://api.auth.productcraft.co/acme-corp/v1/me/permissions \
  -H "Authorization: Bearer <end-user-access-token>"

Response

{
  "role": "member",
  "org_role": null,
  "permissions": ["role.read", "user.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.auth.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-corp';

const JWKS = jose.createRemoteJWKSet(
  new URL(`https://api.auth.productcraft.co/${APP_SLUG}/v1/.well-known/jwks.json`)
);

async function verifyToken(token) {
  const { payload } = await jose.jwtVerify(token, JWKS, {
    issuer:   `https://api.auth.productcraft.co/${APP_SLUG}`,
    audience: APP_SLUG,
  });
  return payload;
}