Quickstart
A runnable walkthrough: create a user, grant them an app, check their access, then turn it off. Every call here is a real endpoint documented in the API Reference — this page just stitches them into one story. Getting Started is where to go for orientation; this page is where to go to see it work.
- 1. Create a user
- 2. Confirm they own nothing yet
- 3. Grant access to an app
- 4. Check what they can actually do
- 5. Assign a role inside the app
- 6. Turn it off
- What this skipped
All requests below assume the Conventions base URL and an admin bearer token:
export SUBSTRATAL_API=https://api.substratalapps.com/v1
export TOKEN=<an access token for a superadmin or support user>
1. Create a user
curl -s -X POST "$SUBSTRATAL_API/users" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "email": "jordan@example.com", "status": "invited" }'
{
"id": "usr_01JAG3Z9X8QS3F6K2M4N5P6R7S",
"email": "jordan@example.com",
"status": "invited",
"...": "..."
}
See Users. A default Profile, Settings, and the member platform role were created alongside this — nothing further to call for those.
2. Confirm they own nothing yet
curl -s "$SUBSTRATAL_API/users/usr_01JAG3Z9X8QS3F6K2M4N5P6R7S/entitlements" \
-H "Authorization: Bearer $TOKEN"
{ "data": [], "page": { "next_cursor": null, "has_more": false } }
3. Grant access to an app
In production this happens automatically off a billing webhook (see Workflows → Purchase → access); here, granting directly the way an admin comp or early-access grant would:
curl -s -X POST "$SUBSTRATAL_API/users/usr_01JAG3Z9X8QS3F6K2M4N5P6R7S/entitlements" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "application_id": "app_timetrack", "source": "admin_grant" }'
{
"id": "ent_01JAGA1B2C3D4E5F6G7H8J9K0L",
"application_id": "app_timetrack",
"status": "active",
"source": "admin_grant",
"...": "..."
}
See Entitlements. status: active is the whole point — this is the on/off switch, and it’s now on.
4. Check what they can actually do
curl -s "$SUBSTRATAL_API/users/usr_01JAG3Z9X8QS3F6K2M4N5P6R7S/applications/app_timetrack/effective-permissions" \
-H "Authorization: Bearer $TOKEN"
{
"user_id": "usr_01JAG3Z9X8QS3F6K2M4N5P6R7S",
"application_id": "app_timetrack",
"entitlement_status": "active",
"effective_permissions": []
}
effective_permissions is empty because no Role has been assigned yet — owning an app and having a role inside it are different axes. See Access Control.
5. Assign a role inside the app
curl -s -X POST "$SUBSTRATAL_API/users/usr_01JAG3Z9X8QS3F6K2M4N5P6R7S/roles/role_timetrack_admin" \
-H "Authorization: Bearer $TOKEN"
Repeat step 4 and effective_permissions now includes whatever role_timetrack_admin grants (e.g. app.timetrack.export).
6. Turn it off
curl -s -X PATCH "$SUBSTRATAL_API/entitlements/ent_01JAGA1B2C3D4E5F6G7H8J9K0L" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
-d '{ "status": "disabled", "disabled_reason": "quickstart_demo" }'
Repeat step 4 again: entitlement_status is now disabled and effective_permissions is back to [] — the Role assignment from step 5 is still there underneath, untouched, ready the moment the entitlement is re-enabled. See Workflows → Admin turns an app off for a user.
What this skipped
Login/token issuance (Auth), the app-launch JWT and introspection that a downstream app calls rather than an admin (Trust Model), and webhook delivery (Webhooks) — all real, all documented, just not needed to see the core access model work end to end.