Port workflows
Build Port workflows: node-based automations made of triggers (self-service forms, context lake events), action nodes (webhook, upsert entity, GitHub/GitLab/Azure DevOps integration actions, Kafka, Cursor Agent, AI), condition and input nodes, JQ templating between nodes, and self-service trigger permissions, authored as workflow JSON against the Port API. Use when asked to create a Port workflow, add a trigger to a workflow, add an action node, wire up a self-service workflow, add branching with a condition node, add an approval step to a workflow, build an event-driven automation in Port, or write workflow JSON. Port workflows are Port's own nodes-and-edges automation graph, not a CI/CD pipeline file like GitHub Actions.From its SKILL.md
npx -y skills add port-labs/port-skills --skill port-workflowsAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 2 stars2 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
10.9 KB, as published. Nobody here has run it
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).
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_IDandCLIENT_SECRET(... menu > Credentials in the Port app), exchanged for a bearer token atPOST 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 always work as a fallback. Searchsearch_port_knowledge_sourcesfor 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.
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 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.
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 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 below.
Complete examples
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 -
event trigger with a JQ filter, an AI node with outputSchema for
structured output, and an entity upsert.
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. - 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. - Permissions:
{}/omitted = Admin only;roles/users/teams= static OR match;policy= dynamic rules overuser,userTeams, orformcontext. Full reference: references/permissions.md. - Auth:
POST /v1/auth/access_tokenwithclientId/clientSecretfor a bearer token; noAuthorizationheader needed when a workflow node itself callshttps://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(defaulttrue),nodes,connections.