Self-Service Catalog
The self-service catalog is one place where people request things — a new laptop, “the CRM Sales role,” access to the Finance app — and where each request flows through the right approval and fulfillment. A single catalog spans every application, while each requester only ever sees the items they are allowed to see.
Every requestable item is the same kind of object regardless of what it delivers. “Request a service” and “request access” look and flow identically to the requester; the difference is only in how the item is fulfilled behind the scenes.
Fulfillment types
Section titled “Fulfillment types”Each catalog item declares one fulfillment_type:
| Type | What happens when requested |
|---|---|
intake_form | Opens a public/intake form — the request is a form submission |
task | Creates a record from a template (e.g. a service_request), optionally firing a business rule |
access_grant | Requests a role/application/table grant, routed through approval, then granted on approval |
Items are stored in system.catalog_items, carry a stable slug, belong to an owning application, and reuse the platform’s global form categories for grouping.
Discovery and requesting
Section titled “Discovery and requesting”The catalog spans applications and lives at a clean, non-app-scoped root:
GET /api/catalog # cross-app discovery, grouped by categoryPOST /api/catalog/{slug}/request # request an item (dispatches on fulfillment_type)GET /api/catalog/requests # the caller's own submitted requests, newest firstGET /api/catalog returns only the items the caller may see, already grouped by category — there is no client-side filtering to bypass.
The visibility engine
Section titled “The visibility engine”Which items a person sees is decided by three tiers, layered on the platform’s existing authorization primitives rather than a separate rule language:
public— shown to any authenticated caller.internal— shown only to callers who hold application-level read on the item’s own application.restricted— shown only to callers who hold a specific grant for that item.
Because visibility is computed from precomputed access — not an author-editable predicate — an item you cannot see is structurally unrequestable: the request endpoint re-runs the identical gate. And because fulfillment always runs through the platform’s normal authorized write path, the catalog can never widen access beyond what the underlying permissions already allow. For finer conditions (e.g. “US-region employees only”), model membership as a dynamic group grant rather than a bespoke rule.
Access requests and approvals
Section titled “Access requests and approvals”An access_grant item ties the catalog to the platform’s approval engine. A typical item declares both the grant it confers and the approval it requires:
{ "fulfillment_type": "access_grant", "fulfillment_config": { "grant": { "target_type": "role", "application": "crm", "role_name": "sales", "max_duration": "P90D" }, "approval": { "approver_type": "role", "approver_role": "crm-manager", "mode": "any", "due_in": "P2D" } }}The flow:
- A principal requests the item. An access-request record is created under the requester’s identity.
- The approval engine routes the request to the named approvers, with escalation, reminders, and one-click approve/reject links.
- On terminal approval, the platform writes the grant — assigning the role (or minting a request-scoped role for application/table grants) to the requester.
- If
max_durationis set, the grant is stamped with an expiry and automatically revoked when it lapses; otherwise it stands until removed. - Every grant and revocation is recorded in the audit log.
target_type may be role, application, or table. Two safeguards apply: an item can only be published for access its author has authority over, and an approver can only approve access they themselves hold — the superuser/operator role is never grantable through the catalog. Revocation also refuses to remove the last holder of an admin role.
Agents as requesters
Section titled “Agents as requesters”Agents and service accounts are first-class principals, so an agent can both request and receive access using the same mechanics as a person. When an agent acts on behalf of a human, the request records the agent as the actor and the human as the sponsor.
My requests
Section titled “My requests”GET /api/catalog/requests returns a caller’s own submitted requests in one uniform shape — access-grant requests unioned with intake-form submissions they created, newest first. Each query is scoped to the caller’s own principal, so no one sees anyone else’s requests.
Related
Section titled “Related”- Workflows & Approvals — the approval engine access requests ride on
- Permissions — the grants an
access_grantitem confers - Dynamic Groups — model finer per-person visibility conditions
- Public Forms — the intake-form fulfillment backend