Install
$ agentstack add skill-port-labs-port-skills-port-workflows ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Port workflows
A workflow is Port's visual, node-based automation layer. Nodes (triggers, actions, conditions, inputs) connect into a graph; running the workflow walks that graph, passing each node's output to the next as JQ-templated data.
Use this skill when
Use this skill to author or edit workflow JSON: adding a self-service or event trigger, adding an action node (webhook, upsert entity, an integration action, Kafka, Cursor Agent, or AI), branching with a condition node, gating on human approval with an input node, or wiring the connections and JQ templates between them. This skill is reference-only: the JSON it produces is valid without a live Port account, though applying it needs API credentials (see [Prerequisites](#prerequisites)).
Out of scope: Port's older, single-step Actions & Automations model (not covered by a skill in this repo yet; see the comparison table for when to use which), blueprint schema design (port-blueprints), and CI/CD pipeline files such as .github/workflows/*.yml, GitHub Actions is a separate product that a workflow can trigger (via INTEGRATION_ACTION) but does not replace.
Prerequisites
- Go over the
port-getting-startedskill first if this is your first time
working with Port.
- To create or update workflows without MCP: a Port
CLIENT_IDand
CLIENT_SECRET (... menu > Credentials in the Port app), exchanged for a bearer token at POST https://api.port.io/v1/auth/access_token.
- If Port's MCP server is connected, this skill can use its workflow tools
(list_workflows, get_workflow, upsert_workflow) to read or apply a workflow directly instead of you copying JSON in by hand, but the raw API calls in [Step 6](#step-6---create-or-update-via-the-port-api) always work as a fallback. Search search_port_knowledge_sources for anything this skill doesn't cover.
- Workflows are in open beta: cross-check unfamiliar fields against
docs.port.io/workflows before shipping.
Step 1 - Sketch the graph and pick trigger type(s)
Precondition: you know what should start the automation and what it should do. Action: decide SELF_SERVE_TRIGGER (a user submits a form on demand), EVENT_TRIGGER (an entity is created, updated, deleted, or a timer property expires), or both, a workflow can define multiple trigger nodes feeding the same downstream graph. Then list the actions in order: webhook calls, entity upserts, integration dispatches, AI steps, with any branching (condition) or human approval (input) points. Fallback: if the trigger is unclear, default to a self-service trigger, it is the safer, opt-in default and easiest to test.
Step 2 - Configure the trigger node(s)
Precondition: trigger type chosen. Action: for SELF_SERVE_TRIGGER, define userInputs.properties (and required); for EVENT_TRIGGER, define event.type and blueprintIdentifier, plus an optional JQ condition to filter which events fire the workflow. Full field reference, including contexts (bolt-menu and create-entity surfacing) and variant, is in [references/node-types.md](references/node-types.md#trigger-nodes). Fallback: never add title, icon, or description to a trigger node, those fields are ignored there and belong on action nodes instead.
Step 3 - Add action and flow nodes
Precondition: trigger is defined. Action: add one node per step. See [references/node-types.md](references/node-types.md) for the full field reference and JSON shape of every node type: WEBHOOK, UPSERT_ENTITY, INTEGRATION_ACTION, KAFKA, CURSOR_AGENT, AI_AGENT, AI, CONDITION, INPUT. Reference upstream data with JQ templates, see [references/data-flow.md](references/data-flow.md). Fallback: if a step just needs to call an HTTP API, prefer WEBHOOK over building a custom integration, and prefer UPSERT_ENTITY over a raw WEBHOOK call to Port's entity API, it is simpler and less error-prone.
Step 4 - Wire connections
Precondition: nodes exist. Action: add a connections entry (sourceIdentifier, targetIdentifier) per edge. CONDITION and INPUT nodes need sourceOutletIdentifier (or fallback: true) on every outgoing connection, matching one of the node's outlets. Trigger nodes can only be sources, never targets. Each node (and each outlet) can have at most one outgoing connection, fan-out is not supported, chain nodes sequentially instead. Fallback: got a fan-out requirement (one node feeding two independent next steps)? Split it into two workflows, or serialize the two branches behind a single downstream node instead of trying to fan out directly.
Step 5 - Set self-service permissions
Precondition: workflow has a SELF_SERVE_TRIGGER node. Action: set permissions on the trigger node's config. Omitted or {} means Admin only. See [references/permissions.md](references/permissions.md) for static (roles/users/teams) and dynamic policy rules (based on user, team, or form-input properties). Fallback: event triggers have no permissions field, event-driven workflows run under the organization's automation identity, not a specific user.
Step 6 - Create or update via the Port API
Precondition: you have a bearer token and a complete workflow JSON body (identifier, nodes, connections, at minimum).
Action: use the endpoints below (workflow_identifier is the workflow's identifier).
| Operation | Method | Path | |---|---|---| | Create | POST | /v1/workflows | | List | GET | /v1/workflows | | Get | GET | /v1/workflows/{workflow_identifier} | | Update (creates a new version) | PUT | /v1/workflows/{workflow_identifier} | | Delete | DELETE | /v1/workflows/{workflow_identifier} | | Trigger a run | POST | /v1/workflows/{workflow_identifier}/runs |
curl -L -X POST 'https://api.port.io/v1/workflows' \
-H "Authorization: Bearer ${ACCESS_TOKEN}" \
-H 'Content-Type: application/json' \
--data @workflow.json
```
Fallback: a `409` on create means the `identifier` is taken, `PUT` an update
instead. A `422` usually means a `connections` entry references a node
identifier that doesn't exist, or a `CONDITION`/`INPUT` connection is missing
`sourceOutletIdentifier`, check [Common pitfalls](#common-pitfalls) below.
## Complete examples
[assets/self-service-deploy-workflow.json](assets/self-service-deploy-workflow.json) -
self-service trigger, environment branching with a `CONDITION` node, and a
Slack notification.
[assets/event-driven-ai-incident-response.json](assets/event-driven-ai-incident-response.json) -
event trigger with a JQ filter, an `AI` node with `outputSchema` for
structured output, and an entity upsert.
[assets/approval-workflow.json](assets/approval-workflow.json) - self-service
trigger gated by an `INPUT` approval step before a production deploy.
## Common pitfalls
| Symptom | Cause | Fix |
|---|---|---|
| `.outputs.my-node.field` silently returns nothing | Hyphens in a node identifier break JQ dot notation (`-` reads as subtraction) | Use bracket notation `.outputs["my-node"].field`, or keep node identifiers snake_case so dot notation works |
| Adding `variables` makes existing output references break | `variables` **replaces** a node's entire default output, `response` and friends disappear | Re-declare anything downstream still needs inside `variables`, e.g. `"response": "{{ .result.response }}"` |
| `CONDITION`/`INPUT` connection has no effect, or workflow rejects it | Missing `sourceOutletIdentifier` (or `fallback: true`) on the connection | Every outgoing connection from a `CONDITION` or `INPUT` node must set `sourceOutletIdentifier` matching one of the node's `outlets`, or `fallback: true` |
| `UPSERT_ENTITY` (or another structured node) after an `AI` node gets nothing | The AI node emitted free-form text, not JSON | Set `outputSchema` (JSON Schema, `type: "object"`) on the AI node, then parse with `{{ .outputs.node_id.response \| fromjson \| .field }}` |
| Event trigger fires but `.outputs.trigger.diff.before` is unexpectedly `null` | `ENTITY_CREATED` has no `before`, `ENTITY_DELETED` has no `after` | Branch on `.outputs.trigger.action` (`CREATE`/`UPDATE`/`DELETE`) before reading `diff.before`/`diff.after` |
| Workflow silently does nothing when a node fails mid-graph | Default `onFailure` is `"terminate"`, the whole run stops | Set `"onFailure": "continue"` on `WEBHOOK`, `UPSERT_ENTITY`, `KAFKA`, or `INTEGRATION_ACTION` nodes that shouldn't block the rest of the run |
| GitHub Actions workflow never fires | Used `WEBHOOK` to call the GitHub API by hand, or used the wrong field name for status reporting | Use `INTEGRATION_ACTION` with `integrationProvider: "github-ocean"`, and `reportWorkflowStatus` (not `reportStatus`) to get status back in Port |
## Quick reference
- Node types: `SELF_SERVE_TRIGGER`, `EVENT_TRIGGER` (triggers); `WEBHOOK`,
`UPSERT_ENTITY`, `INTEGRATION_ACTION`, `KAFKA`, `CURSOR_AGENT`, `AI_AGENT`,
`AI` (actions); `CONDITION`, `INPUT` (flow control). Full reference:
[references/node-types.md](references/node-types.md).
- Data access: `{{ .outputs["node_id"].field }}` (previous node output),
`{{ .outputs.trigger.field }}` (always aliases the trigger that fired),
`{{ .secrets["name"] }}`, `{{ .workflowRun.trigger.by.email }}`. Full
reference: [references/data-flow.md](references/data-flow.md).
- Permissions: `{}`/omitted = Admin only; `roles`/`users`/`teams` = static OR
match; `policy` = dynamic rules over `user`, `userTeams`, or `form`
context. Full reference: [references/permissions.md](references/permissions.md).
- Auth: `POST /v1/auth/access_token` with `clientId`/`clientSecret` for a
bearer token; no `Authorization` header needed when a workflow node itself
calls `https://api.port.io`.
- Workflow top-level fields: `identifier` (required, ≤60 chars,
`^[A-Za-z0-9@_:-]+$`), `title`, `icon`, `description`, `category` (groups
it in the UI), `allowAnyoneToViewRuns` (default `true`), `nodes`,
`connections`.
## Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- **Author:** [port-labs](https://github.com/port-labs)
- **Source:** [port-labs/port-skills](https://github.com/port-labs/port-skills)
- **License:** MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.