> ## Documentation Index
> Fetch the complete documentation index at: https://docs.lumos.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Create an Entitlement That Provisions Through a Group

> Create an AppStore entitlement on an app, and bind a provisioning workflow that adds each person granted it to a group, such as an Okta group.

```python Python theme={null}
import os
import time

import requests

BASE_URL = "https://api.lumos.com"
APP_ID = "<APP_ID>"  # The app to add the entitlement to. It must be in the AppStore.
GROUP_ID = "<GROUP_ID>"  # The Lumos ID of the group to add each person to.
LABEL = "Deployer"  # The name requesters see in the AppStore.

session = requests.Session()
session.headers["Authorization"] = f"Bearer {os.environ['LUMOS_API_TOKEN']}"


def call(method, path, **kwargs):
    """Call a v1 endpoint, waiting out rate limits."""
    while True:
        response = session.request(method, f"{BASE_URL}{path}", **kwargs)
        if response.status_code == 429:
            time.sleep(float(response.headers.get("Retry-After", 1)))
            continue
        response.raise_for_status()
        return response.json() if response.content else None


# Check that the app offers the add_to_group step for entitlement provisioning.
params = {"app_id": APP_ID, "type": "entitlement-provisioning"}
steps = set()
while True:
    page = call("GET", "/v1/workflow-steps", params=params)
    steps.update(step["step"] for step in page["workflow_steps"])
    if not page.get("next_page_token"):
        break
    params["page_token"] = page["next_page_token"]
if "add_to_group" not in steps:
    raise SystemExit("This app doesn't offer the add_to_group step.")

# Create the entitlement, then hide it until its workflow is bound.
entitlement = call("POST", "/v1/entitlements", json={"app_id": APP_ID, "label": LABEL})
settings_path = f"/v1/entitlements/{entitlement['id']}/appstore-settings"
call("PATCH", settings_path, json={"visible_in_appstore": False})

# Create the provisioning workflow, and bind it to the entitlement.
workflow = call(
    "POST",
    "/v1/workflows",
    json={
        "name": f"Add to group for {LABEL}",
        "type": "entitlement-provisioning",
        "app_id": APP_ID,
        "steps": [{"step": "add_to_group", "inputs": {"group_id": GROUP_ID}}],
    },
)
call(
    "PUT",
    f"/v1/entitlements/{entitlement['id']}/provisioning-workflow",
    json={"workflow_id": workflow["id"]},
)

# Show the entitlement in the AppStore.
call("PATCH", settings_path, json={"visible_in_appstore": True})
print(f"entitlement: {entitlement['id']}")
print(f"workflow: {workflow['id']}")
```

## Prepare the request

The script reads your API token from the `LUMOS_API_TOKEN` environment variable, so the token never
appears in the code. Create the token as a user with the Admin role, as
[Before you start](https://docs.lumos.com/reference/v1/guides/app-store-setup.md#before-you-start) explains. The script uses
Python 3 and the `requests` library (`pip install requests`).

```bash theme={null}
export LUMOS_API_TOKEN='lsk_your_token'
python create_entitlement.py
```

`call()` sends each request and handles one failure itself. When a response is a `429`, meaning
you're over the rate limit, it waits the number of seconds in the `Retry-After` header, then tries
again. Any other error raises and stops the script.

## Choose the app and the group

Set `APP_ID` to the app's `id` from [List Apps](https://docs.lumos.com/api-reference/v1/apps/list-apps.md). The app must
already be in the AppStore, because an entitlement has AppStore settings only while its app is in
the AppStore. [Set up an app in the AppStore](https://docs.lumos.com/reference/v1/guides/app-store-setup.md) covers adding
it.

Set `GROUP_ID` to the group's Lumos `id`, not its ID in the app it comes from.
[Find a Group by Name](https://docs.lumos.com/recipes/find-a-group-by-name.md) prints both. If you have only the group's ID
from Okta or Google Workspace, find the group with the `integration_specific_id` filter on
[List Groups](https://docs.lumos.com/api-reference/v1/groups/list-groups.md).

## Check the step

The steps a workflow can use differ from app to app. The script first reads the app's catalog with
[List Workflow Steps](https://docs.lumos.com/api-reference/v1/workflows/list-workflow-steps.md). With `type` set to
`entitlement-provisioning`, the catalog lists only the steps that type of workflow accepts. Each
entry's `input_schema` describes the inputs its step takes.

## Create the entitlement

[Create Entitlement](https://docs.lumos.com/api-reference/v1/entitlements/create-entitlement.md) creates the entitlement
visible in the AppStore. Anyone allowed to request the app could then request it before it has a
provisioning workflow. The script hides it right away with
[Update Entitlement AppStore Settings](https://docs.lumos.com/api-reference/v1/entitlements/update-entitlement-appstore-settings.md),
and shows it again in the last step.

The entitlement uses the app's approval workflow. To give it different approvers, set an override
with
[Set Entitlement Approval Workflow Override](https://docs.lumos.com/api-reference/v1/entitlements/set-entitlement-approval-workflow-override.md).

## Bind the provisioning workflow

[Create Workflow](https://docs.lumos.com/api-reference/v1/workflows/create-workflow.md) stores the workflow, and
[Set Entitlement Provisioning Workflow](https://docs.lumos.com/api-reference/v1/entitlements/set-entitlement-provisioning-workflow.md)
binds it to the entitlement. Lumos runs the workflow when it grants the entitlement to someone.
[The Workflow object](https://docs.lumos.com/reference/v1/objects/workflow.md) covers workflow types and steps.

To take people out of the group when their access ends, also bind a deprovisioning workflow with
[Set Entitlement Deprovisioning Workflow](https://docs.lumos.com/api-reference/v1/entitlements/set-entitlement-deprovisioning-workflow.md).
List Workflow Steps with `type` set to `entitlement-deprovisioning` shows the steps your app offers
for it.
