Enforcer v3 docs / Enforcer Quickstart

03Quickstart
03

Get going

Quickstart

The live sandbox is open, no setup needed. Base URL is https://api.instruxi.dev/api/v1/enforcer and the sandbox tenant code is DLXZ-57TL-FS75. Watch the 30 second walkthrough, run it yourself right here, or do it by hand below.

Watch, using the sandbox30 sec
Try it live

Walk into the front desk

A real sign in, a real identity check, and a real access gate, shown as a little world. Sign in, prove who you are with a sandbox ID check, and watch the locked room open.

This is the front desk for your app. Sign in to walk through.

Real sign-ins here are logged so the Enforcer team can follow up. No marketing spam.

    Prefer the terminal? The same calls by hand are right below.

    Sign in to the sandbox

    Real email, real token. Ask for a one time code, exchange it for a session token, then call the API with that token.

    1, request a code
    curl -X POST "https://api.instruxi.dev/api/v1/enforcer/auth/otp/request" \
      -H "Content-Type: application/json" \
      -d '{ "email": "you@example.com", "tenant_code": "DLXZ-57TL-FS75" }'
    
    # a 6 digit OTP is emailed to you, valid for 10 minutes

    Response

    { "success": true, "message": "code sent" }
    2, log in
    curl -X POST "https://api.instruxi.dev/api/v1/enforcer/auth/login" \
      -H "Content-Type: application/json" \
      -d '{ "provider": "email_otp", "email": "you@example.com", "otp": "123456", "tenant_code": "DLXZ-57TL-FS75" }'
    
    # returns a token plus a refresh_token. Honour expires_at; this sandbox issues ~15 min

    Response

    {
      "success": true,
      "data": {
        "token": "eyJhbGciOi...",
        "refresh_token": "eyJhbGciOi...",
        "account": { "id": "acc_01J...", "email": "you@example.com", "tenant_id": "ten_01J...", "role": "user" }
      }
    }
    3, you now have a session
    # the login response gives you data.token and data.account.id
    # send the token on every call
    curl "https://api.instruxi.dev/api/v1/enforcer/auth/me" \
      -H "Authorization: Bearer <token>"

    If you would rather hold a server credential than a session token, POST /api-keys with {"name":"..."} and the bearer from the previous step mints one. The plaintext key is in that response and nowhere else, so capture it there.

    See the gate flip

    Groups are how a capability gets unlocked. Create one, add yourself, and you are through the gate. In the sandbox you do it by hand to watch it work. In production a KYC pass or your own policy adds people to the group automatically.

    1, create a group (the gate)
    # the sandbox is shared, so pick a name and slug that are your own
    curl -X POST "https://api.instruxi.dev/api/v1/enforcer/groups" \
      -H "Authorization: Bearer <token>" \
      -H "Content-Type: application/json" \
      -d '{ "name": "Verified yourname", "slug": "verified-yourname", "description": "passed the check", "is_public": false }'
    
    # response: data.id is your new group id
    2, add yourself to it
    curl -X POST "https://api.instruxi.dev/api/v1/enforcer/groups/<group_id>/members" \
      -H "Authorization: Bearer <token>" \
      -H "Content-Type: application/json" \
      -d '{ "account_id": "<your account id from login>" }'

    Response

    { "success": true }
    3, confirm you are through
    curl "https://api.instruxi.dev/api/v1/enforcer/groups/<group_id>/members" \
      -H "Authorization: Bearer <token>"
    
    # your account is in the member list. that membership is the gate.

    That is the whole idea. Group membership flips a capability from blocked to allowed with no code change on the route. Swap the manual add for a KYC pass and you have the production flow.

    refresh the token when it expires
    curl -X POST "https://api.instruxi.dev/api/v1/enforcer/auth/refresh" \
      -H "Content-Type: application/json" \
      -d '{ "refresh_token": "<refresh_token>" }'

    Response

    { "success": true, "data": { "token": "eyJhbGciOi...", "refresh_token": "eyJhbGciOi..." } }

    Two ways to build on it:

    The fastest path

    Inside an AI coding tool such as Cursor, Claude Code, Lovable, or Windsurf, you describe the app you want and the Enforcer connector provisions a real tenant and scaffolds a working app wired to it.

    prompt
    build me a banking app with login and KYC

    The connector creates a tenant, mints the keys, wires up the auth methods you asked for, and leaves you with code that already talks to Enforcer. From there you keep building in plain language or drop down to the API below.

    The connector is the front door for new builds. When you need exact control, the by hand path uses the same endpoints under the hood.

    The sandbox above signs you into a shared tenant. To stand up your own tenant and gate an action, here is the full sequence. Self serve tenant creation is gated per account, so if the first call returns missing_token or a 403 that is the gate, not a mistake on your part. Your base URL is https://api.instruxi.dev (live now). Replace the placeholder values as you go. Values like $TENANT_CODE, $GROUP_ID and $ACCOUNT_ID come from the responses of earlier steps, noted inline below. Single quoted JSON does not expand shell variables, so substitute the real values by hand when you run these.

    1. Create your tenant, no human in the loop

      Self serve tenant creation makes the caller the tenant admin and hands back the new tenant. The response carries the tenant (note its id and code) and your tenant admin session token. Use that token as $ADMIN_JWT and the tenant code as $TENANT_CODE below. The exact field names are in the worked reference, generated from the live spec.

      request
      curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/tenants/self-serve" \
        -H "Content-Type: application/json" \
        -d '{
          "name": "Acme Pay",
          "admin_email": "you@acme.com"
        }'
      
      # response: the new tenant (id, code) plus your tenant-admin session token
    2. Mint a server API key

      Use the tenant admin token from the step above as the bearer. The plaintext key is shown once, store it now.

      request
      curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/api-keys" \
        -H "Authorization: Bearer $ADMIN_JWT" \
        -H "Content-Type: application/json" \
        -d '{ "name": "server-key" }'

      Response

      {
        "success": true,
        "data": { "key": "ek_live_9f3c...a71b", "api_key": { "id": "key_01J...", "name": "server", "key_prefix": "ek_live_9f3c" } }
      }
    3. Register a user and request an OTP

      Register the account, then request an email OTP scoped to your tenant. In development the OTP is returned to you so you can script the flow.

      request
      # register
      curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/auth/register" \
        -H "Content-Type: application/json" \
        -d '{ "provider": "email_otp", "email": "user@acme.com" }'
      
      # request the OTP
      curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/auth/otp/request" \
        -H "Content-Type: application/json" \
        -d '{ "email": "user@acme.com", "tenant_code": "$TENANT_CODE" }'
    4. Log in to get a user JWT

      Exchange the OTP for a user bearer token plus a refresh token.

      request
      curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/auth/login" \
        -H "Content-Type: application/json" \
        -d '{
          "provider": "email_otp",
          "email": "user@acme.com",
          "otp": "123456",
          "tenant_code": "$TENANT_CODE"
        }'
      
      # response includes: token, refresh_token, expires_at, account
    5. Add the user to the gating group

      The group is the capability gate. Membership is what unlocks the route the group protects. List your tenant's groups to find the one you want to gate on (its id is $GROUP_ID), then add the account ($ACCOUNT_ID from the login response above). The API key goes in the X-API-Key header. Groups are provisioned with your tenant, confirm with your Enforcer contact how your gating groups are seeded.

      request
      # list this tenant's groups, take the id of the one you gate on
      curl "$ENFORCER_BASE_URL/api/v1/enforcer/groups" \
        -H "X-API-Key: $API_KEY"
      
      # add the account to it -> capability now unlocked
      curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/groups/$GROUP_ID/members" \
        -H "X-API-Key: $API_KEY" \
        -H "Content-Type: application/json" \
        -d '{ "account_id": "$ACCOUNT_ID" }'

    That last step is the whole point. The user existed and could log in, but the gated action was forbidden. Adding them to the group flips that to allowed, with no code change on the route. Swap the manual add for the KYC module reporting a pass and you have the production verification flow.