Enforcer v3 docs / Enforcer Recipes

06Recipes
06

How to

Recipes

Short, copyable answers to the things you will do in the first week.

Gate a page on KYC

This is the core mechanic. The route is protected by group membership. A new user is not in the group, so the action is forbidden. When the KYC module reports a pass, you add the account to the verified group and the same route now allows them.

request
# on KYC pass, add the account to the gating group
curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/groups/$GROUP_ID/members" \
  -H "X-API-Key: $API_KEY" \
  -d '{ "account_id": "$ACCOUNT_ID" }'

# to revoke access later, remove them
curl -X DELETE "$ENFORCER_BASE_URL/api/v1/enforcer/groups/$GROUP_ID/members/$ACCOUNT_ID" \
  -H "X-API-Key: $API_KEY"

The route itself does not change. You are only changing who is in the group.

Add a login method

Enforcer supports passkey, email OTP, phone OTP over SMS, Google, SIWE, and Privy. The provider field on login and register selects the method. Email OTP login looks like this:

request
curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/auth/login" \
  -d '{ "provider": "email_otp", "email": "user@acme.com", "otp": "123456", "tenant_code": "$TENANT_CODE" }'

For Google or Privy you pass the provider token in the token field instead of an OTP. Set which method your tenant accepts with the dedicated auth-provider call:

request
curl -X PUT "$ENFORCER_BASE_URL/api/v1/enforcer/tenants/$TENANT_ID/auth-provider" \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{ "provider": "email_otp" }'

Confirm the exact passkey and SIWE begin and finish routes against the live spec for your build.

Invite teammates

Create an invite for your tenant, then the invitee accepts it to join.

request
# create an invite (tenant admin)
curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/tenants/$TENANT_ID/invites" \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -d '{ "email": "teammate@acme.com" }'

# invitee accepts
curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/auth/invites/accept" \
  -H "Authorization: Bearer $USER_JWT" \
  -d '{ "code": "$INVITE_CODE" }'

To set what a teammate can do, assign them a role with PUT /accounts/{id}/role. List the available roles with GET /roles.

Theme your app

Set the tenant name, logo, and theme with a single patch.

request
curl -X PATCH "$ENFORCER_BASE_URL/api/v1/enforcer/tenants/$TENANT_ID" \
  -H "Authorization: Bearer $ADMIN_JWT" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "Acme Pay",
    "logo_url": "https://acme.com/logo.png",
    "theme": { "primary": "#6ce992" }
  }'
Switch a user between tenants

Because the person sits above per tenant users, one human can hold memberships in several tenants. List them, then switch. A switch returns a token scoped to the new tenant.

request
# list this person's tenant memberships
curl "$ENFORCER_BASE_URL/api/v1/enforcer/auth/tenants" \
  -H "Authorization: Bearer $USER_JWT"

# switch into another tenant
curl -X POST "$ENFORCER_BASE_URL/api/v1/enforcer/auth/tenant/switch" \
  -H "Authorization: Bearer $USER_JWT" \
  -d '{ "tenant_id": "$OTHER_TENANT_ID" }'

To join a tenant the person is not yet a member of, use POST /auth/tenant/join with a tenant_code.