Import a workflow
POST/
Makes a draft workflow from an export: GoRunner’s, or another automation tool’s.
Needs an API token with the Write permission for workflows (workflow:write).
Parameters
Headers
Idempotency-KeyOptional · stringUp to 255 characters. The same request with the same key within 24 hours answers the first result again instead of importing a second copy. See Idempotency.
Body
The exported file, as it is: GoRunner’s envelope, the whole answer of Export a workflow, or just its workflow object; or a workflow exported from another automation tool. Up to 5 MiB.
gorunnerExportOptional · stringIn GoRunner’s envelope: always
workflow.versionOptional · integerIn GoRunner’s envelope: always
1.workflowOptional · objectIn GoRunner’s envelope: the name, description, tags and definition. Empty name: “Imported Workflow”. A name over 255 characters is shortened, and tags past the limits are cut, rather than refused.
What carries over
The import is a new draft with a new ID. It gets none of the original’s traffic: it has no webhook address until you first update it or publish it.
Every app and action must exist here. In a GoRunner export, a step without a pieceVersion is pinned to the app’s oldest version, the one it always ran; write the version on a definition of your own (a new Code step: 2.0.0). An unfinished draft imports fine.
A field that held a secret arrives empty: the export has only [hidden secret: kept as set].
Connection IDs come over as they are. They don’t exist on another installation, so point each step at a connection there.
To see first what another tool’s export becomes, preview the import.
The report
Each node that runs in the original file is listed once in importReport.nodes, with its outcome:
mapped: an app here does the job. substituted: a general app does the same work, such as HTTP Request. parked: nothing here does it, so the step is a No Op holding the original settings, to replace.
ready says whether the step runs as imported. leftovers counts settings that had nowhere to go. needsAttention counts steps that can’t run yet.
The steps, their order and names, branches and their conditions, loops, an HTTP call’s method and address, a schedule and a code step’s code carry over. A trigger that can’t be read exactly becomes manual. Notes on the canvas are counted in warnings.
Responses
- 201Created
The new draft, with its
definitionRev. From another tool’s export, it also hasimportReport. The body isn’t JSON, the tree has a key it doesn’t take (the message names it), a step’s name or display name is over 255 characters, or a condition has an operator that doesn’t exist (the message lists the ones that do).
- 403
FORBIDDENThe token lacks the Write permission for workflows.
- 409
CONFLICTA request with this
Idempotency-Keyis still in progress, or its answer was too large to keep. Try again shortly. The file isn’t an export GoRunner reads (the message says what it found), it holds no definition, or
schemaVersionis something other than 2, 0 or absent.A step names an app or action this installation doesn’t have. The message names it.
This
Idempotency-Keywas already used with a different request.A GoRunner export has a step on an app this workspace may not use. Another tool’s export never answers this: such steps are parked.
Nothing was imported. Try again.
GoRunner couldn’t check which apps this workspace may use. Nothing changed. Try again.
Any call can also answer: 401 UNAUTHORIZED · 401 TOKEN_ENDED · 403 TOKEN_SURFACE_OFF · 403 FEATURE_OFF · 403 EMBED_NOT_ALLOWED · 403 EMBED_NOT_AVAILABLE · 403 ACCOUNT_SUSPENDED · 413 PAYLOAD_TOO_LARGE · 415 INVALID_CONTENT_TYPE · 429 RATE_LIMITED · 503 BUSY · 503 PAUSED · 503 SERVICE_UNAVAILABLE. See Errors.
Request
GORUNNER="https://api.gorunner.io"
GORUNNER_TOKEN="grt_…" # needs Write for workflows
curl -X POST "$GORUNNER/api/workflows/import" \
-H "Authorization: Bearer $GORUNNER_TOKEN" \
-H "Idempotency-Key: $(uuidgen)" \
-H "Content-Type: application/json" \
-d '{
"gorunnerExport": "workflow",
"version": 1,
"workflow": {
"name": "Order intake",
"tags": [
"orders"
],
"definition": {
"schemaVersion": 2,
"trigger": {
"name": "trigger",
"type": "webhook",
"pieceId": "webhook",
"actionId": "catch_webhook",
"config": {
"methods": [
"POST"
]
},
"valid": true
},
"settings": {}
}
}
}'const GORUNNER = "https://api.gorunner.io";
const TOKEN = process.env.GORUNNER_TOKEN;
const res = await fetch(
`${GORUNNER}/api/workflows/import`,
{
method: "POST",
headers: {
Authorization: `Bearer ${TOKEN}`,
"Idempotency-Key": crypto.randomUUID(),
"Content-Type": "application/json",
},
body: JSON.stringify({
gorunnerExport: "workflow",
version: 1,
workflow: {
name: "Order intake",
tags: ["orders"],
definition: {
schemaVersion: 2,
trigger: {
name: "trigger",
type: "webhook",
pieceId: "webhook",
actionId: "catch_webhook",
config: {
methods: ["POST"],
},
valid: true,
},
settings: {},
},
},
}),
},
);
const { data } = await res.json();import os, uuid, requests
GORUNNER = "https://api.gorunner.io"
TOKEN = os.environ["GORUNNER_TOKEN"]
res = requests.post(
f"{GORUNNER}/api/workflows/import",
headers={
"Authorization": f"Bearer {TOKEN}",
"Idempotency-Key": str(uuid.uuid4()),
},
json={
"gorunnerExport": "workflow",
"version": 1,
"workflow": {
"name": "Order intake",
"tags": ["orders"],
"definition": {
"schemaVersion": 2,
"trigger": {
"name": "trigger",
"type": "webhook",
"pieceId": "webhook",
"actionId": "catch_webhook",
"config": {
"methods": ["POST"],
},
"valid": True,
},
"settings": {},
},
},
},
)
data = res.json()["data"]Response · 201 Created, shortened, from another tool’s export
{
"data": {
"id": "6e9a1c3f-8b2d-4a57-9e0c-3f7b5d1a8c42",
"name": "Order intake",
"status": "draft",
"definitionRev": 1,
"triggerType": "webhook",
"importReport": {
"format": "external",
"name": "Order intake",
"nodes": [
{
"sourceName": "HTTP Request",
"sourceType": "httpRequest",
"outcome": "mapped",
"pieceId": "http",
"stepName": "http_request",
"ready": true,
"leftovers": 1
}
],
"mappedCount": 2,
"substitutedCount": 0,
"parkedCount": 0,
"needsAttention": 0,
"stepsWithLeftovers": 1
}
}
}Every field
The body takes one of 2 forms.
Body, an export file, as Export a workflow gives it
gorunnerExportRequired · stringFormat marker; the value is the literal string
workflow, not a boolean.One of
workflow.versionRequired · integerExport FORMAT version — 1. Unrelated to
definition.schemaVersion, which is 2.One of
1.exportedAtOptional · timeWhen the export was produced. Informational; import ignores it.
workflowRequired · objectThe portable payload inside an export envelope. Import also accepts this object on its own, without the envelope around it.
nameOptional · stringOn import, an empty or absent name becomes "Imported Workflow".
descriptionOptional · stringtriggerTypeOptional · stringOverridden by whatever
definition.triggersays, so it is effectively informational.One of
manual,cron,webhook,callable,event,error,run_finished,poll.tagsOptional · array of stringsTrimmed and deduplicated case-insensitively on import.
definitionRequired · objectThe workflow definition, and the single most important shape in this API.
A workflow definition: its fields are in Workflow definition.
Body, the workflow alone, without the envelope
nameOptional · stringOn import, an empty or absent name becomes "Imported Workflow".
descriptionOptional · stringtriggerTypeOptional · stringOverridden by whatever
definition.triggersays, so it is effectively informational.One of
manual,cron,webhook,callable,event,error,run_finished,poll.tagsOptional · array of stringsTrimmed and deduplicated case-insensitively on import.
definitionRequired · objectThe workflow definition, and the single most important shape in this API.
A workflow definition: its fields are in Workflow definition.
Response · 201 Created
dataobjectidUUIDnamestringdescriptionstringdefinitionobjectThe workflow definition, and the single most important shape in this API.
A workflow definition: its fields are in Workflow definition.
statusstringdraft= never published.active= live, triggers registered and the webhook URL reachable.paused= was live, now switched off. Server-owned: it moves only through publish/unpublish, never through a create or update body.One of
draft,active,paused.triggerTypestringMirrors
definition.trigger.typefor dispatch. Derived server-side from the definition by every update and publish, so treat it as read-only in practice. A create that sends its own value keeps it until then, unchecked.One of
manual,cron,webhook,callable,event,error,run_finished,poll.cronExpressionstring or nullNothing derives or reads this: the schedule runs from the trigger’s own
config, andnextRunAtprojects from it too. Null unless a caller wrote one withPUT /api/workflows/{id}.webhookTokenstring or nullThe secret segment of the public webhook URL. Made by the first update of a webhook workflow, or at publish; absent until then (so absent in the answers of create, duplicate and import). Returned ONLY to a caller holding
workflow:run: byGET /api/workflows/{id}and in the answers of update, publish, unpublish and restore (rotate answers it to its caller). It is never present on a list row, an export or a version snapshot. It is a bearer capability against an unauthenticated endpoint — possessing it is the ability to fire this workflow in production. Rotate it withPOST /api/workflows/{id}/webhook/rotate.latestVersionIdUUID or nullThe published snapshot production executes. Absent means nothing has ever been published.
definitionRevintegerOptimistic-lock revision, bumped by every definition save. Populated by
GET /api/workflows/{id}, by the update response and by the answers of create, duplicate and import; absent (0) on list rows. Send it back asexpectedRevon an update to refuse a clobbering write.hasUnpublishedChangesbooleanThe draft has moved ahead of the published version. Computed on the single-workflow read and on every list row; always
falsefor a workflow never published.folderIdUUID or nullAbsent/null = uncategorized. Changed only through
PUT /api/workflows/{id}/folder.tagsarray of stringsChanged only through
PUT /api/workflows/{id}/tagsor the bulkaddTagsoperation — never through the workflow update body.testPayloadsobject or nullThe saved test-fixture library, stored outside the definition so publish and version restore never reset it.
pinnedDataobject or nullThe pin library (
{"pins": {"<stepName>": {"data": …, "pinnedAt": …, "configHash": …, "staleAt": …, "pieceId": …}}}). A pinned step emits its fixture instead of executing, and only on test runs.configHash,staleAtandpieceIdare the server’s: a step whose config changed keeps its pin marked stale, and a stale pin is not replayed (the step runs); a step whose app was replaced loses it.workspaceIdUUIDStamped from the credential’s scope; never accepted from a request body.
orgIdUUIDcreatedByUUID or nullcreatedByNamestringList rows only: the creator’s name, else their email address. Absent when the creator is unknown or not a member of the organization (staff who built it on a support visit, someone since removed), and on every row for an embedded person whose installation switched
presenceoff (an embedded person is never told an address).createdAttimeupdatedAttimeWhen the workflow itself was last edited. Publishing, pausing, tagging and filing it are not edits and leave it alone.
failingbooleanList rows only: the workflow is live (
status: active) and its latest FINISHED production run (one that worked or failed) failed. A later run that is waiting, running, held or was cancelled changes nothing, and test runs never count. A paused or draft workflow is never failing.failingSincetimeOnly on a failing row: when the first production run failed after the last one that worked (runs cancelled, held or running in between neither break nor begin the streak). Left out for a caller without
run:read.failingRunobjectOnly on a failing row: the run that decided it, its latest finished production run, with why it failed. It is
lastRununless a later run is waiting, running, held or was cancelled. Withoutrun:readit carriesstatusandstartedAtalone.idUUIDstatusstringOne of
failed.startedAttimefinishedAttime or nullerrorobjectWhy it failed, as
lastRun.errorsays it.stepstring or nullstepKeystring or nullmessagestringsummaryobjectList rows only: what a list draws instead of the definition.
triggerobject or nullThe chosen trigger; null while none is chosen.
pieceIdstringactionIdstringdisplayNamestringpiecesarray of objectsThe apps the workflow uses, each once, in walk order (the trigger first), with their display names.
idstringnamestringstepCountintegerSteps after the trigger, across branch arms, loop bodies and error paths.
needsSetupintegerSteps (and a chosen trigger) the editor marks to configure:
valid: false, or no app and action chosen.runs7darray of integersList rows without
view=summaryonly: counted runs per UTC day over the last seven days, oldest first (production runs that are not a sub-flow’s, cancelled runs left out).nextRunAttimeNext fire time of an active schedule workflow, projected from its cron in the trigger’s own timezone. Populated by the list endpoint.
lastRunobject or nullThe most recent PRODUCTION run’s outcome (test runs never count). Populated on list rows only. Without
run:readit carriesstatusandstartedAtalone.idUUIDfinishedAttime or nullerrorobjectOnly when
statusisfailed: why.stepstring or nullThe display name of the step it failed at; null when the reason names no step.
stepKeystring or nullmessagestringThe reason’s first clause, at most 160 characters.
statusstringOne of
pending,running,completed,failed,cancelled,paused.startedAttimeimportReportobjectWhat a translation from another product could and could not carry. Every node of the source file that asks to RUN appears in
nodesexactly once; canvas comments (sticky notes and other notes) are counted inwarningsinstead, since they are not steps. One bound, reported when it is reached: branches and loops nested deeper than this product’s own structural limit are not followed, because the alternative is letting a file decide how deep the server recurses. Real flows nest a handful of levels.formatstringDetected from the file: always
external(another automation tool’s export).One of
external.namestringThe name the source file gave the workflow.
nodesarray of objectssourceNamestringWhat the source file called it, so a person can find it there.
sourceTypestringThe source node’s own short type, without the source tool’s package prefix, e.g.
slackorhttpRequest.outcomestringmapped— an app here does that job.substituted— no app for it, but a generic one does the same work.parked— nothing here does it, so the step is an invalid No Op carrying the original settings.One of
mapped,substituted,parked.pieceIdstringThe GoRunner app it became.
no-opon a parked step.stepNamestringIts name here — what an expression referencing it says.
readybooleanWhether the step runs as imported. False asks for a visit: a connector can be MAPPED and still need its fields, because a foreign node’s settings rarely mean the same thing here.
notestringThe honest sentence for this row.
leftoversintegerHow many of the source’s own settings are still on this node because there was no field for them. Present even when
readyis true — a step can run and still be unfinished.mappedCountintegersubstitutedCountintegerparkedCountintegerneedsAttentionintegerHow many steps cannot run as imported — the number that decides whether this is a draft to finish or a draft to publish.
stepsWithLeftoversintegerHow many nodes still hold settings the translation could not place, whether or not they can run. NOT the same question as needsAttention: a step can run exactly as imported and still be carrying settings the original had, so a flow can be fully valid and still have translation left in it.
warningsarray of stringsFile-level things the reader should know: a shape that had to be flattened (the source ran a fan-out in parallel, this product runs one chain), nodes nothing reached, comments left behind, expressions that need rewriting.