Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.prisme.ai/llms.txt

Use this file to discover all available pages before exploring further.

The access-manager module provides four groups of functions :
  1. Service account management — Create, rotate, delete service accounts and issue JWT tokens. Restricted to privileged workspaces defined via PRIVILEGED_WORKSPACES.
  2. Org API key management — Create, list, rotate, revoke org API keys scoped to the privileged workspace. Restricted to privileged workspaces.
  3. Product bindings — CRUD operations on the product_bindings collection to link resources to users, orgs, or groups. Available to any workspace.
  4. Access checking — High-level checkAccess function that combines permissions, scopes, and bindings to determine if a caller can access a resource. Available to any workspace.
Service account and org API key functions (getServiceAccountToken, createServiceAccount, rotateServiceAccountSecret, deleteServiceAccount, createOrgApiKey, listOrgApiKeys, rotateOrgApiKey, deleteOrgApiKey) are only available to workspaces listed in the PRIVILEGED_WORKSPACES environment variable. Binding and access check functions are available to all workspaces.

Service Account Functions

getServiceAccountToken — Get a JWT token for a service account

- run:
    module: access-manager
    function: getServiceAccountToken
    parameters:
      orgSlug: "{{orgSlug}}"
      serviceAccountSlug: "{{saSlug}}"
      create: true
      expiresIn: 3600
    output: tokenResult
ParameterTypeRequiredDescription
orgSlugstringyesOrganization slug
serviceAccountSlugstringyesService account slug
createbooleannoWhen true, creates the service account if it doesn’t exist and rotates the secret if needed. When false or omitted, only works with a cached secret.
namestringnoDisplay name for the service account (used on creation)
roleSlugstringnoRole to assign. Must be in the workspace’s allowedRoleSlugs. Defaults to the workspace’s defaultRoleSlug.
expiresInnumbernoToken TTL in seconds
Returns the token response including accessToken, tokenType, expiresAt, permissions, and scopes.

createServiceAccount — Create a new service account

- run:
    module: access-manager
    function: createServiceAccount
    parameters:
      orgSlug: "{{orgSlug}}"
      serviceAccountSlug: "{{saSlug}}"
      name: "My Agent"
      roleSlug: "agent-standard"
    output: result
ParameterTypeRequiredDescription
orgSlugstringyesOrganization slug
serviceAccountSlugstringyesService account slug
namestringnoDisplay name
roleSlugstringnoRole to assign (validated against allowed roles)
Returns the created service account including slug and clientSecret. If the service account already exists, returns { slug } without error.

rotateServiceAccountSecret — Rotate a service account’s client secret

- run:
    module: access-manager
    function: rotateServiceAccountSecret
    parameters:
      orgSlug: "{{orgSlug}}"
      serviceAccountSlug: "{{saSlug}}"
    output: result
ParameterTypeRequiredDescription
orgSlugstringyesOrganization slug
serviceAccountSlugstringyesService account slug
Returns the new clientSecret.

deleteServiceAccount — Delete a service account

- run:
    module: access-manager
    function: deleteServiceAccount
    parameters:
      orgSlug: "{{orgSlug}}"
      serviceAccountSlug: "{{saSlug}}"
    output: result
ParameterTypeRequiredDescription
orgSlugstringyesOrganization slug
serviceAccountSlugstringyesService account slug

Cache behavior

The module caches client secrets in memory after creation or rotation. This cache is automatically invalidated when:
  • A service account is deleted (clears secret + permissions cache)
  • A service account secret is rotated (clears secret cache)
  • A service account is updated (clears permissions cache)
Cache invalidation is event-driven and applies to all runtime instances simultaneously.

Service Account Example

slug: get-agent-token
name: Get Agent Token
do:
  # Create (or reuse) a service account and get a JWT token
  - run:
      module: access-manager
      function: getServiceAccountToken
      parameters:
        orgSlug: "{{orgSlug}}"
        serviceAccountSlug: "agent-{{agentId}}"
        name: "Agent {{agentId}}"
        create: true
        expiresIn: 3600
      output: tokenResult

  # Use the token to call an API
  - fetch:
      url: "{{config.apiUrl}}/resources"
      method: GET
      headers:
        Authorization: "Bearer {{tokenResult.accessToken}}"
      output: resources

Org API Key Functions

These functions let a privileged workspace mint and manage org-level API keys on behalf of its users — typically to expose a programmatic key tied to a single product resource (an agent, a workflow, etc.) without granting the user direct orgs:apikeys:manage permission.

Security model

The runtime enforces strict isolation so a privileged workspace can only see and manage keys it owns:
  • ownerType is force-prefixed with the calling workspace slug (e.g. the DSUL passes agent, the runtime stores agent-factory:agent). The DSUL cannot spoof another workspace’s ownerType.
  • listOrgApiKeys force-prefixes the ownerType filter the same way, so listings only ever return keys minted by the calling workspace.
  • rotateOrgApiKey / deleteOrgApiKey fetch the target key first via GET /v2/orgs/:orgSlug/api-keys/:keyId and refuse the operation unless the existing ownerType starts with ${workspaceSlug}:.
  • permissions and scopes are forwarded verbatim to api-gateway. The DSUL is responsible for prefixing them; the runtime then validates each value against the privileged-workspace allowlist (see config below) — so a workspace can declare permissions/scopes of other workspaces only if the operator has explicitly listed them.

Configuration

Add an apiKeys block to the workspace’s entry in PRIVILEGED_WORKSPACES:
{
  "agent-factory": {
    "defaultRoleSlug": "agent-standard",
    "allowedRoleSlugs": ["agent-standard", "agent-admin"],
    "apiKeys": {
      "allowedPermissions": [
        "agent-factory:agents:read",
        "agent-factory:agents:write"
      ],
      "allowedScopes": ["agent-factory:agents:*"]
    }
  }
}
FieldTypeDescription
apiKeys.allowedPermissionsstring[]Fully-qualified permission patterns the workspace may mint. Trailing * is supported (e.g. agent-factory:agents:*). The runtime rejects any permission not matching at least one pattern.
apiKeys.allowedScopesstring[]Fully-qualified scope patterns the workspace may attach to its API keys. Same glob semantics as allowedPermissions.
If apiKeys is omitted, the workspace cannot create API keys.

createOrgApiKey — Mint a new API key

- run:
    module: access-manager
    function: createOrgApiKey
    parameters:
      orgSlug: "{{auth.orgSlug}}"
      slug: "agent-{{agentId}}-{{run.correlationId}}"
      name: "API Key – {{agent.name}}"
      permissions:
        - agent-factory:agents:read
        - agent-factory:agents:write
      scopes:
        - agent-factory:agents:{{agentId}}
      ownerType: agent
      ownerId: "{{agentId}}"
      expiresAt: "2026-12-31T23:59:59Z"
    output: created
ParameterTypeRequiredDescription
orgSlugstringyesOrganization slug owning the key
slugstringyesUnique short id of the key within the org
namestringyesDisplay name
permissionsstring[]yesFully-qualified permissions (DSUL prefixes them; runtime validates against allowedPermissions)
scopesstring[]noFully-qualified scopes (validated against allowedScopes)
ownerTypestringyesBare owner type (e.g. agent). The runtime force-prefixes it with the workspace slug.
ownerIdstringnoOwner identifier (typically the resource id)
expiresAtstringnoISO-8601 expiration date
Returns { id, slug, apiKey, name, permissions, expiresAt }. The raw apiKey is shown only on creation — store it immediately.

listOrgApiKeys — List the workspace’s API keys

- run:
    module: access-manager
    function: listOrgApiKeys
    parameters:
      orgSlug: "{{auth.orgSlug}}"
      ownerType: agent
      ownerId: "{{agentId}}"
      limit: 50
    output: keys
ParameterTypeRequiredDescription
orgSlugstringyesOrganization slug
ownerTypestringyesBare owner type. The runtime force-prefixes with the workspace slug; api-gateway has no wildcard support, so this is an exact filter.
ownerIdstringnoRestrict to keys for a single owner
limitnumbernoPage size (default 50)
pagenumbernoPage index (1-based)
Returns { results: OrgApiKey[], total }. Keys minted by other workspaces in the same org are never returned.

rotateOrgApiKey — Regenerate a key’s secret

- run:
    module: access-manager
    function: rotateOrgApiKey
    parameters:
      orgSlug: "{{auth.orgSlug}}"
      keyId: "{{keyId}}"
      expiresAt: "2027-01-01T00:00:00Z"
    output: rotated
ParameterTypeRequiredDescription
orgSlugstringyesOrganization slug
keyIdstringyesSlug/id of the key to rotate
expiresAtstringnoNew expiration (keeps the previous one if omitted)
Returns the same shape as createOrgApiKey (with the new raw apiKey). Throws if the key’s ownerType doesn’t start with ${workspaceSlug}:.

deleteOrgApiKey — Revoke a key

- run:
    module: access-manager
    function: deleteOrgApiKey
    parameters:
      orgSlug: "{{auth.orgSlug}}"
      keyId: "{{keyId}}"
    output: deleted
ParameterTypeRequiredDescription
orgSlugstringyesOrganization slug
keyIdstringyesSlug/id of the key to delete
Returns { success: true }. Like rotateOrgApiKey, refuses if the key isn’t owned by the calling workspace.

Org API Key Example

End-to-end automation that mints, lists and revokes an API key tied to a specific agent (single endpoint exposed via webhook in the agent-factory workspace):
slug: v1/agents/agent_id/api-keys
name: API/v1/Agents/agent_id/Api-keys
when:
  endpoint: v1/agents/:agent_id/api-keys
do:
  - _auth:
      agent_id: '{{pathParams.agent_id}}'
      resource: agents
      action: share
      output: auth
  - conditions:
      '{{method}} = "POST"':
        # Prefix each requested permission with this workspace's slug so the
        # runtime allowlist (PRIVILEGED_WORKSPACES.agent-factory.apiKeys) accepts it.
        - set:
            name: _prefixed_permissions
            value: []
        - repeat:
            'on': '{{body.permissions}}'
            do:
              - set:
                  name: _prefixed_permissions[]
                  value: agent-factory:{{item}}
        - run:
            module: access-manager
            function: createOrgApiKey
            parameters:
              orgSlug: '{{auth.orgSlug}}'
              slug: '{{body.slug}}'
              name: '{{body.name}}'
              permissions: '{{_prefixed_permissions}}'
              scopes:
                - agent-factory:agents:{{pathParams.agent_id}}
              ownerType: agent
              ownerId: '{{pathParams.agent_id}}'
              expiresAt: '{{body.expiresAt}}'
            output: result
      '{{method}} = "GET"':
        - run:
            module: access-manager
            function: listOrgApiKeys
            parameters:
              orgSlug: '{{auth.orgSlug}}'
              ownerType: agent
              ownerId: '{{pathParams.agent_id}}'
            output: result
output: '{{result}}'

Product Bindings Functions

Product bindings associate resources (agents, workflows, etc.) to principals (users, orgs, groups) within a workspace. All binding functions automatically scope queries to the caller’s workspaceId — it cannot be overridden.

findBindings — Query bindings

- run:
    module: access-manager
    function: findBindings
    parameters:
      query:
        resourceType: agents
        principalType: user
        principalId: "{{userId}}"
      options:
        pagination:
          limit: 50
          page: 0
        sort:
          createdAt: desc
    output: bindings
ParameterTypeRequiredDescription
queryobjectyesFilter fields matching ProductBinding (e.g. resourceType, resourceId, principalType, principalId, orgSlug)
options.paginationobjectno{ page, skip, limit }
options.sortobjectnoSort fields (e.g. { createdAt: 'desc' })
options.fieldsarraynoFields to return
Returns an array of binding documents.

findAndCountBindings — Query bindings with total count

- run:
    module: access-manager
    function: findAndCountBindings
    parameters:
      query:
        resourceType: agents
      options:
        pagination:
          limit: 20
          page: 0
    output: result
# result.items = [...], result.total = 42
Same parameters as findBindings. Returns { items: [...], total: number }.

countBindings — Count matching bindings

- run:
    module: access-manager
    function: countBindings
    parameters:
      query:
        resourceType: agents
        orgSlug: "{{orgSlug}}"
    output: count
ParameterTypeRequiredDescription
queryobjectyesFilter fields
Returns a number.

insertBinding — Create a binding

- run:
    module: access-manager
    function: insertBinding
    parameters:
      data:
        resourceType: agents
        resourceId: "{{agentId}}"
        principalType: user
        principalId: "{{targetUserId}}"
        orgSlug: "{{orgSlug}}"
        grantedBy: "{{userId}}"
        email: "{{targetEmail}}"
    output: result
# result.acknowledged = true, result.insertedId = "..."
ParameterTypeRequiredDescription
data.resourceTypestringyesResource type (e.g. agents, workflows)
data.resourceIdstringyesResource identifier
data.principalTypestringyesuser, org, or group
data.principalIdstringyesPrincipal identifier
data.orgSlugstringyesOrganization slug
data.grantedBystringyesUser who granted the binding
data.emailstringnoEmail for user bindings
data.roleSlugstring | nullnoOptional role slug controlling which actions this binding grants. See Role-based bindings. When null or omitted, the binding grants every action except delete.
The workspaceId and workspaceSlug are set automatically from the caller’s context. Emits a runtime.bindings.created event. A unique constraint prevents duplicate bindings for the same (workspaceId, resourceType, resourceId, principalType, principalId).

updateBinding — Update an existing binding

- run:
    module: access-manager
    function: updateBinding
    parameters:
      query:
        resourceType: agents
        resourceId: "{{agentId}}"
        principalType: user
        principalId: "{{targetUserId}}"
      data:
        roleSlug: editor
    output: result
# result.matchedCount = 1, result.modifiedCount = 1
ParameterTypeRequiredDescription
queryobjectyesFilter to match the binding(s) to update
data.roleSlugstring | nullnoNew role slug to apply. Pass null to clear the role (binding will then grant all actions except delete).
Only roleSlug can be updated through this function — other fields are immutable. Emits a runtime.bindings.updated event when at least one binding is modified.

deleteOneBinding — Delete a single binding

- run:
    module: access-manager
    function: deleteOneBinding
    parameters:
      query:
        resourceType: agents
        resourceId: "{{agentId}}"
        principalType: user
        principalId: "{{targetUserId}}"
    output: result
# result.deletedCount = 1
ParameterTypeRequiredDescription
queryobjectyesFilter to match the binding to delete
Emits a runtime.bindings.deleted event if a binding was deleted.

deleteManyBindings — Delete multiple bindings

- run:
    module: access-manager
    function: deleteManyBindings
    parameters:
      query:
        resourceType: agents
        resourceId: "{{agentId}}"
    output: result
# result.deletedCount = 5
ParameterTypeRequiredDescription
queryobjectyesFilter to match bindings to delete
Emits a runtime.bindings.deleted.many event if any bindings were deleted.

Workspace cleanup

When a workspace is deleted, all its bindings are automatically removed.

Access Check Function

The checkAccess function provides a high-level access control check that combines three sources: permissions (from run.permissions), scopes (from run.scopes), and bindings (from the product_bindings collection). This replaces the need for complex DSUL-based permission checking.

checkAccess — Check resource access

- run:
    module: access-manager
    function: checkAccess
    parameters:
      resourceType: agents
      resourceId: "{{agentId}}"
      action: read
    output: access
All parameters are optional. When called without resourceType/action, it only checks authentication and returns isWorkspaceAdmin.
ParameterTypeRequiredDescription
resourceTypestringnoResource type (e.g. agents, workflows). Must be set together with action.
resourceIdstringnoSpecific resource ID. Requires resourceType.
actionstringnoAction to check (typically read, write, share, delete, manage, but any string is accepted — must match the keys in roles[*].permissions). Must be set together with resourceType.
listbooleannoWhen true, returns the list of granted resource IDs instead of a single grant
rolesobjectnoRole definitions for binding role enforcement. Record<string, { name?: string, permissions: string[] }>. Required as soon as any matching binding has a roleSlug — otherwise checkAccess throws. See Role-based bindings.

Return value

FieldTypeDescription
grantedbooleanWhether access is granted
reasonstringWhy access was granted: permission, wildcard-scope, scope, or binding:{principalType} (e.g. binding:user, binding:org, binding:group). When the matching binding carries a roleSlug, the reason becomes binding:{principalType}:{roleSlug} (e.g. binding:user:editor).
grantedIdsstring[]Only in list mode — merged set of IDs from scopes and bindings
hasWildcardScopebooleantrue if the caller has wildcard scope (sees all resources of this type)
isWorkspaceAdminbooleantrue if the caller has * or {workspaceSlug} manage permission
errorobjectOnly when granted is false — contains error (code) and message (human-readable)
The error object follows the same shape as _auth automations:
error.errorerror.messageWhen
UnauthorizedAuthentication requiredNo authenticated user (no userId and no org API key)
ForbiddenAccess denied: missing permission '{workspaceSlug}:{resourceType}:{action}'User is authenticated but has no matching permission

Resolution order

  1. Authentication — The caller must have a userId (user session) or an orgSlug (org API key). If neither is present, returns Unauthorized immediately.
  2. Permissions — Checked from run.permissions (set by the token’s role). The function checks in order:
    • * with manage → workspace admin
    • {workspaceSlug} with manage → workspace admin
    • {workspaceSlug}:{resourceType} with manage or {action} → access
    • If no permission matches → returns Forbidden immediately (scopes and bindings are not checked)
  3. Scopes — Parsed from run.scopes (only if permission was granted):
    • *, {workspaceSlug}:*, or {workspaceSlug}:{resourceType}:* → wildcard scope (all resources)
    • {workspaceSlug}:{resourceType}:{id} → adds id to scoped IDs
  4. Bindings — Looked up in product_bindings for the caller’s identity (userId, org, groups). Only checked for single resource mode when the scope doesn’t match. Each matching binding is then evaluated against the requested action through role-based bindings.

Role-based bindings

Bindings can carry a roleSlug to restrict which actions they grant. The role definitions themselves live outside the binding — they must be passed to checkAccess via the roles parameter (typically loaded from config.roles):
- run:
    module: access-manager
    function: checkAccess
    parameters:
      resourceType: agents
      resourceId: "{{agentId}}"
      action: write
      roles:
        owner:
          name: Owner
          permissions: [read, write, share, delete]
        admin:
          name: Admin
          permissions: [read, write, share]
        editor:
          name: Editor
          permissions: [read, write]
        reader:
          name: Reader
          permissions: [read]
    output: access
Resolution rules applied to each candidate binding:
Binding roleSlugroles parameterBehavior
null / unset(any)Binding grants every action except delete. Used for legacy bindings or “full collaborator” semantics.
set (e.g. editor)provided, slug existsAction must be in roles[slug].permissions — otherwise the binding is ignored and checkAccess continues to the next binding (or returns Forbidden).
setnot provided at allcheckAccess throws — passing roles is required as soon as any binding could be role-typed.
setprovided but slug missingThe binding is ignored (treated as having no permissions).
checkAccess only stops at the first binding that grants the requested action. If a user has multiple bindings (e.g. reader and editor), the most permissive applicable one wins. When the binding is granted, the reason field reflects the role: binding:user:editor, binding:org:admin, etc.
Keep your role catalog in config.roles so it can be referenced consistently from every endpoint that calls checkAccess (and from insertBinding / updateBinding for validation).

Modes

Auth-only (no resourceType, no action):
  • Returns { granted: true, isWorkspaceAdmin } — just confirms the caller is authenticated
Unauthenticated caller:
  • Returns { granted: false, error: { error: 'Unauthorized', message: 'Authentication required' } }
Single resource (resourceId provided):
  • If wildcard scope → { granted: true, reason: 'wildcard-scope', hasWildcardScope: true, isWorkspaceAdmin }
  • If scoped ID match → { granted: true, reason: 'scope', hasWildcardScope: false, isWorkspaceAdmin }
  • If binding found and the binding’s roleSlug allows the action → { granted: true, reason: 'binding:{principalType}' or 'binding:{principalType}:{roleSlug}', hasWildcardScope: false, isWorkspaceAdmin }
  • Otherwise → { granted: false, hasWildcardScope: false, error: { error: 'Forbidden', message: '...' } }
List mode (list: true, no resourceId):
  • If wildcard scope → { granted: true, grantedIds: [], hasWildcardScope: true } (caller sees everything)
  • Otherwise → { granted: true, grantedIds: [...], hasWildcardScope: false } (merged+deduplicated scoped IDs + binding IDs)
Permission-only (resourceType + action, no resourceId, no list):
  • Returns { granted: true, reason: 'permission', hasWildcardScope, isWorkspaceAdmin }

Access check examples

Guard a specific resource

- run:
    module: access-manager
    function: checkAccess
    parameters:
      resourceType: agents
      resourceId: "{{agentId}}"
      action: read
    output: access

# access.error is set when granted=false (UNAUTHORIZED or FORBIDDEN)
- conditions:
    "{{access.error}}":
        - break:
            automation: true
            output:
              error: "{{access.error.error}}"
              message: "{{access.error.message}}"

Check auth + admin status only

- run:
    module: access-manager
    function: checkAccess
    output: access

# access.granted = true if authenticated
# access.isWorkspaceAdmin = true if user has * or workspace-level manage

List all accessible resources

- run:
    module: access-manager
    function: checkAccess
    parameters:
      resourceType: agents
      action: read
      list: true
    output: access

# If access.hasWildcardScope is true, query all agents without filter
# Otherwise, filter agents by access.grantedIds
- conditions:
    "{{access.hasWildcardScope}}":
        - fetch:
            url: "{{config.apiUrl}}/agents"
            output: agents
    default:
        - fetch:
            url: "{{config.apiUrl}}/agents?ids={{access.grantedIds | join ','}}"
            output: agents