Skip to content

Run a pipeline ​

Use a configured pipeline to process a deal and retrieve its output submissions. The sequence below supplies case figures to a manual-input step, then reads all output values.

Connection and input ​

Use Bash, curl and jq. Set these variables in the shell. Choose a pipeline with one manual-input form containing numeric fields named TotalAssets and AnnualRevenue, followed by its configured analysis steps. Replace the field names and figures in VALUES for your form.

sh
export API_BASE="https://YOUR_API_HOST"
export TOKEN="YOUR_ACCESS_TOKEN"

Run the following sections in order in the same Bash shell, or save them together as run-pipeline.sh and run bash run-pipeline.sh.

sh
set -euo pipefail
: "${API_BASE:?Set the API host root without /v2}"
: "${TOKEN:?Set the access token or API key}"
request() {
  curl --fail-with-body --silent --show-error \
    -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" "$@"
}

List the pipelines and select the ID of the pipeline to run:

sh
request "$API_BASE/v2/pipelines/" | jq '.[] | {id, name, steps}'
PIPELINE_ID="YOUR_PIPELINE_ID"

1. Start the run ​

Create a deal to set DEAL_ID, then start the run:

sh
: "${PIPELINE_ID:?Set the configured pipeline ID}"
: "${DEAL_ID:?Set the deal ID}"
request -X POST "$API_BASE/v2/pipelines/runs" \
  -d "$(jq -n --arg p "$PIPELINE_ID" --arg d "$DEAL_ID" '{pipelineId:$p,dealId:$d}')" > run.json
RUN_ID=$(jq -er '.runId' run.json)
jq '{runId, dispatchAttempt, links}' run.json
if [ "$(jq -r '.dispatchAttempt.outcome' run.json)" != accepted ]; then
  exit 1
fi

The receipt returns the canonical runId and links for status and outputs.

2. Find the required input form ​

Read requiredFormId from the manual-input blocker, then read the draft submission the run created for that step from its outputs. The data read supplies the item IDs to populate.

Run status returns pipelineId and each step's pipelineStepId. Match these to the pipeline definition and its steps[].id. Use runStepId for an execution. Each steps[].attempt is the current positive-integer attempt, used to fence document uploads against step resets. Select a configured role or label through its definition, and use the IDs for the link. Names are display text.

sh
while :; do
  request "$API_BASE/v2/pipelines/runs/$RUN_ID" > status.json
  BLOCKER=$(jq -c '[.steps[] | . as $step | .blockers[] |
    select(.blockerType == "missing_manual_input") |
    {runStepId:$step.runStepId, formId:.blockerDetails.requiredFormId}][0] // empty' status.json)
  [ -z "$BLOCKER" ] || break
  case "$(jq -r '.status' status.json)" in
    failed|cancelled|completed) jq . status.json; exit 1 ;;
  esac
  sleep 2
done
FORM_ID=$(jq -er '.formId' <<< "$BLOCKER")
RUN_STEP_ID=$(jq -er '.runStepId' <<< "$BLOCKER")
SUBMISSION_ID=$(request "$API_BASE/v2/pipelines/runs/$RUN_ID/outputs" \
  | jq -er --arg s "$RUN_STEP_ID" --arg f "$FORM_ID" \
    'first(.outputs[] | select(.runStepId == $s and .formId == $f)) | .submissionId')
request "$API_BASE/v2/submissions/$SUBMISSION_ID/data" > input.json

3. Supply input values and attach the submission ​

sh
VALUES='{"TotalAssets":1250000,"AnnualRevenue":840000}'
while IFS=$'\t' read -r FIELD VALUE; do
  ITEM_ID=$(jq -er --arg name "$FIELD" \
    '[.groups[].items[] | select(.formField.name == $name) | .id] | if length == 1 then .[0] else error("Expected one matching field") end' input.json)
  request -X PATCH "$API_BASE/v2/submissions/$SUBMISSION_ID/items/$ITEM_ID" \
    -d "$(jq -n --argjson v "$VALUE" '{value:$v}')" > /dev/null
done < <(jq -r 'to_entries[] | [.key, (.value | tojson)] | @tsv' <<< "$VALUES")
request -X PATCH "$API_BASE/v2/submissions/$SUBMISSION_ID" -d '{"status":"final"}' > /dev/null
request -X POST "$API_BASE/v2/pipelines/runs/$RUN_ID/steps/$RUN_STEP_ID/manual-inputs" \
  -d "$(jq -n --arg s "$SUBMISSION_ID" '{submissionId:$s}')"

The blocker stays open until a final submission exists, so mark it final before attaching. The attach endpoint takes an existing submission ID. Use the exact form required by the step.

A pipeline can have several manual-input steps. Repeat sections 2 and 3 until no manual-input blocker remains. For an extraction step waiting for documents, use the separate run-step upload operation. It supplies that step's document dependency without creating or finalising a manual-input submission.

4. Read the result ​

sh
while :; do
  request "$API_BASE/v2/pipelines/runs/$RUN_ID" > status.json
  case "$(jq -r '.status' status.json)" in
    completed) break ;;
    failed|cancelled) jq . status.json; exit 1 ;;
  esac
  sleep 2
done
request "$API_BASE/v2/pipelines/runs/$RUN_ID/outputs" > outputs.json
jq '.outputs[] | {runStepId, formId, submissionId, outputRole, links}' outputs.json
while IFS= read -r DATA_PATH; do
  request "$API_BASE$DATA_PATH" | jq '{submissionId, groups}'
done < <(jq -r '.outputs[].links.data' outputs.json)

Each output identifies its run step, form and submission. Select outputs by those IDs when your application needs a particular result. links.data returns the current editable values for that output submission.

Credit memo ​

Use an analysis step configured to write the memo into a string-long field named CreditMemo. Set MEMO_FORM_ID to that step's output formId from steps[].outputForms[] in the pipeline list.

GET /v2/pipelines/runs/{runId}/outputs identifies the memo submission:

json
{
  "runId": "run-1",
  "pipelineId": "pipeline-1",
  "dealId": "deal-1",
  "runRefId": null,
  "outputs": [
    {
      "runStepId": "run-step-1",
      "stepId": "step-1",
      "stepRole": "analysis",
      "outputRole": null,
      "label": null,
      "status": "completed",
      "submissionId": "memo-submission-1",
      "formId": "memo-form-1",
      "links": { "data": "/v2/submissions/memo-submission-1/data" }
    }
  ]
}

Read the first output matching MEMO_FORM_ID through links.data and save its memo value as UTF-8 text:

sh
MEMO_FORM_ID="YOUR_MEMO_FORM_ID"
MEMO_DATA_PATH=$(jq -er --arg form "$MEMO_FORM_ID" \
  'first(.outputs[] | select(.formId == $form)) | .links.data' outputs.json)
request "$API_BASE$MEMO_DATA_PATH" > memo.json
jq -er '.groups[].items[] | select(.formField.name == "CreditMemo") | .value' \
  memo.json > credit-memo.txt

The data response contains the memo in groups[].items[]:

json
{
  "id": "memo-item-1",
  "formField": {
    "id": "memo-field-1",
    "name": "CreditMemo",
    "type": "string-long"
  },
  "value": "Credit assessment\nThe case meets the configured lending criteria."
}

Document input ​

Start the run before supplying its documents. SeaUpload binds to each extraction step's unique label inside that existing process. The SDK resolves the executable run step, uploads its batch and refreshes the run's outputs.

For server integrations, upload directly to the run step. Several labelled extraction steps can receive their documents within the same run. A complete upload resolves only its selected missing_upload dependency; other blockers may remain.

Read results through the existing run's output links. Submission uploads remain available for integrations that work directly with submissions.

Creation receipt ​

dispatchAttempt.outcomeAction
acceptedPoll the run status
not-sent, BLOCKER_SYNC_FAILEDRetain the run ID for operator recovery
unknown, DISPATCH_UNCONFIRMEDRead run status and retain the run ID if processing does not progress

Creation is non-idempotent. An uncertain response must be reconciled using the existing run ID before another creation request. pending, running, blocked and hibernating await progress; completed is successful; failed and cancelled are terminal failures.