> For the complete documentation index, see llms.txt.
Skip to main content

Check out Port for yourself ➜ 

Dynamic permissions

Workflow permissions allow you to control who can see and execute a workflow from Port's self-service page. Workflows that a user does not have permission to execute are hidden entirely from their self-service page.

Related documentation

This page covers workflow-specific execute permissions (who can trigger a run). For controlling who can view or edit workflow definitions, see workflow catalog and ownership. For general Port RBAC, see manage users & teams.

Common use cases

  • Allow only members of a specific team to execute a workflow.
  • Restrict a workflow to Admin only.
  • Dynamically grant access based on the user's properties (e.g., department, team).
  • Allow access only when a specific form input value is provided.
  • Restrict a workflow to specific execution sources, such as the Port UI only, API only, or MCP only.

How it works

Permissions are set in the trigger node's config when creating or updating a workflow via POST /workflows or PUT /workflows/:id.

Admin always has access

Admins can always execute all workflows, regardless of the permissions configuration.

Default behavior

permissions valueWho can execute
Not set or {}Admin only
{ "roles": ["Member"] }Admin + Member
{ "users": [...] }Admin + listed users
{ "teams": [...] }Admin + listed teams
{ "policy": {...} }Admin + users matching the policy
Evaluation order
  • Static fields (roles, users, teams) are checked first using OR logic. Any match grants access.
  • Policy is only evaluated if static fields don't match.
  • Organization machine tokens bypass static checks when no policy is present. Personal access tokens are evaluated like interactive users and do not bypass static checks.

Configuration

Permissions are set inside the trigger node's config object:

{
"identifier": "my-workflow",
"title": "My Workflow",
"nodes": [
{
"identifier": "trigger",
"title": "Start",
"config": {
"type": "SELF_SERVE_TRIGGER",
"permissions": {
"roles": ["Member"],
"users": [],
"teams": []
},
"userInputs": {
"properties": {}
}
}
}
],
"connections": []
}

Static assignment

Grant access to specific roles, users, or teams. Use Port user IDs (not emails).

{
"permissions": {
"roles": ["Member"],
"users": ["user-id-1", "user-id-2"],
"teams": ["platform-team", "sre-team"]
}
}

Admin only

Two ways to restrict to Admin only:

Explicit empty object:

{
"permissions": {}
}

Omit the field:

{
"config": {
"type": "SELF_SERVE_TRIGGER",
"userInputs": { "properties": {} }
}
}

Dynamic permissions (policy)

Use policy for runtime evaluation based on user properties, team membership, or form inputs.

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "user", "property": "department" },
"operator": "=",
"value": "engineering"
}
]
}
}
}

The policy object provides an additional access path beyond static fields.

Policy structure

{
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "<context>", "property": "<property>" },
"operator": "<operator>",
"value": "<value>"
}
]
}
}

Contexts

ContextResolves to
userThe executing user's properties (from the _user blueprint)
userTeamsThe executing user's team properties (from the _team blueprint)
formThe workflow trigger's form input values
workflowRunWorkflow run metadata, including the execution source (UI, API, or MCP)

Use the source property with the workflowRun context to compare against a specific execution source value.

Dot notation for entity inputs

For entity-type form inputs, use dot notation to access entity properties:

SyntaxType
inputNamestring
inputName.$identifierstring
inputName.$titlestring
inputName.$teamarray - use contains/notContains
inputName.propertyNameactual blueprint type

Deep traversal (input.relation.nested) is not supported, use mirror or calculation properties instead.

Execution source

Port evaluates workflow permissions against the execution source, which reflects how the run is being invoked:

SourceWhen it applies
UIA user triggers the workflow from the Port UI, such as the self-service hub, an entity bolt menu, or a pre-filled workflow URL.
APIA workflow run is created through the API, including organization machine tokens and personal access tokens.
MCPA workflow is invoked through the Port MCP server, for example by an AI agent.

When Port lists self-service triggers or validates whether a user can execute a workflow, it resolves the execution source from the request context. Use the workflowRun context with the source property in a policy when you need different access rules for UI, API, and MCP invocations.

Operators

OperatorDescription
=Equals
!=Not equals
>, <, >=, <=Comparisons (numbers/strings)
inScalar property is in array value
notInScalar property is not in array value
containsArray property contains scalar value (use for $team)
notContainsArray property does not contain scalar value
containsAnyArray property shares at least one element with another array value
emptyValue is empty
notEmptyValue is not empty

Examples

Allow only a specific team (static)

{
"permissions": {
"teams": ["platform-team"]
}
}

Allow based on team membership (policy)

Only allow users who are a member of the platform-team team (requires $identifier property on the _team blueprint):

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "userTeams", "property": "$identifier" },
"operator": "contains",
"value": "platform-team"
}
]
}
}
}

Restrict to members of entity's owning team

Only allow users who belong to the same team that owns the selected service entity:

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "form", "property": "service.$team" },
"operator": "containsAny",
"value": { "context": "userTeams", "property": "$identifier" }
}
]
}
}
}

Restrict based on entity team ownership

Only allow when the selected cluster entity belongs to platform-team:

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "form", "property": "cluster.$team" },
"operator": "contains",
"value": "platform-team"
}
]
}
}
}

Restrict based on form input

Only allow when user selects production environment:

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "form", "property": "environment" },
"operator": "=",
"value": "production"
}
]
}
}
}

Restrict to UI execution only

Only allow users to run the workflow from the Port UI. API and MCP invocations are blocked:

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "workflowRun", "property": "source" },
"operator": "=",
"value": "UI"
}
]
}
}
}

Block MCP execution

Allow UI and API invocations, but block runs triggered through the Port MCP server:

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "workflowRun", "property": "source" },
"operator": "!=",
"value": "MCP"
}
]
}
}
}

Restrict based on user property

Only allow users in the engineering department (requires department property on _user blueprint):

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "user", "property": "department" },
"operator": "=",
"value": "engineering"
}
]
}
}
}

Restrict to specific entity

Only allow when a specific cluster is selected (requires cluster entity input):

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "form", "property": "cluster.$identifier" },
"operator": "=",
"value": "prod-cluster"
}
]
}
}
}

Combined static + policy

The member role always has access. Non-members need to be in the SRE department:

{
"permissions": {
"roles": ["Member"],
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "user", "property": "department" },
"operator": "=",
"value": "sre"
}
]
}
}
}
Evaluation order

Static fields are checked first. Policy only runs if static fields don't match.

Multiple conditions (OR)

Allow if the user is in the engineering or SRE department:

{
"permissions": {
"policy": {
"combinator": "or",
"rules": [
{
"property": { "context": "user", "property": "department" },
"operator": "=",
"value": "engineering"
},
{
"property": { "context": "user", "property": "department" },
"operator": "=",
"value": "sre"
}
]
}
}
}

Or use in operator for cleaner syntax:

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "user", "property": "department" },
"operator": "in",
"value": ["engineering", "platform", "sre"]
}
]
}
}
}

Restrict to members of entity's owning team (dynamic)

Allow execution only if the executing user belongs to one of the teams that own the selected entity. Requires an entity-type form input (e.g., service).

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "form", "property": "service.$team" },
"operator": "containsAny",
"value": { "context": "userTeams", "property": "$identifier" }
}
]
}
}
}

form.service.$team resolves to the array of teams that own the selected service entity. userTeams.$identifier resolves to the array of all team identifiers the executing user belongs to. The rule passes if those two arrays share at least one element.


Restrict to managers of team-owned entities

Allow execution only if the executing user is the manager of the team that owns the selected entity.

Add a mirror property to your blueprint that follows the relation path to the manager's identifier (e.g., service -> _team -> manager(_user) -> $identifier), then compare it against the executing user's identity:

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
// Mirror property follows: service -> _team -> manager (_user) -> $identifier
"property": {
"context": "form",
"property": "service.team_manager_id"
},
"operator": "=",
"value": { "context": "user", "property": "$identifier" }
}
]
}
}
}

form.service.team_manager_id resolves to the manager's email (via the mirror property). user.$identifier resolves to the executing user's email. The rule passes only if they match.


Restrict to managers or team leads

Allow execution only if the executing user has a specific role. Requires a role (or equivalent) property on the _user blueprint:

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
"property": { "context": "user", "property": "role" },
"operator": "in",
"value": ["manager", "team-lead", "director"]
}
]
}
}
}
Blueprint properties

The properties available in the user and userTeams contexts depend on what properties you have defined on your _user and _team blueprints. You can add custom properties such as role, department, or is_manager to those blueprints and use them in workflow permissions.


Restrict to senior roles

Allow execution only for senior engineers (for example, to gate production deployments):

{
"permissions": {
"policy": {
"combinator": "and",
"rules": [
{
// Requires a 'seniority' property on the _user blueprint
"property": { "context": "user", "property": "seniority" },
"operator": "in",
"value": ["senior", "staff", "principal"]
}
]
}
}
}
Validation vs permissions

If you want to block execution based on catalog state (for example, preventing a workflow from running if an entity with the given name already exists), that is a runtime validation, not a permissions check. Use a condition node as the first step in your workflow to query the catalog and stop the run if the condition is not met.