Skip to main content

Linker Workflow Orchestration API

Gateway prefix: ${API_BASE}/linker/...

Backend service: ai-link-service

Core controllers:

  • WorkflowController (/api/workflows)
  • TriggerController (/api/triggers)
  • TaskReportController (/api/task)

Positioning and typical scenarios

AI-Link provides a visual, orchestratable workflow engine that links:

  • AI capabilities (LLM, function calling)
  • Data capabilities (query, write)
  • External systems (HTTP APIs, messages, webhooks)

into observable and traceable business processes, for example:

  • approval flows (automatic decisions + human approvals)
  • CRM lead routing and follow-up reminders
  • ETL preprocessing and AI quality checks

Core concepts

  • Workflow
    • Node graph composed of Start/End/HTTP/Condition/Loop/Code/Approval/LLM nodes, etc.
    • Each workflow has a unique apiKey used for external execution.
  • Trigger
    • Decoupled from workflows; multiple triggers can be configured for the same workflow.
    • Types: WEBHOOK, CRON, MQ
  • Execution
    • Each run produces an executionId for logs and approvals.
  • HumanTask
    • Approval nodes in the workflow produce pending tasks that require human action before continuing.

Workflow management and execution (/api/workflows)

Create workflow

  • POST /linker/workflows
  • Request body: Workflow object

Key fields:

  • name: workflow name
  • description: description
  • nodes: node array (start, end, http, approval, llm, etc.)
  • edges: edge array describing wiring between nodes
  • metadata: arbitrary JSON metadata

Node data/meta structures vary by node type. It is recommended to use the Linker visual editor to generate/maintain them instead of editing JSON manually.

Example:

curl -X POST "${API_BASE}/linker/workflows" \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"name": "Order Approval Workflow",
"apiKey": "order_approval_v1",
"description": "Order approval flow",
"nodes": [],
"edges": []
}'

Common API list

  • Create: POST /linker/workflows
  • Update: PUT /linker/workflows/<id>
  • Get details: GET /linker/workflows/<id>
  • Paged list: GET /linker/workflows?name=&status=&page=&size=
  • Publish: POST /linker/workflows/<id>/publish
  • Activate: POST /linker/workflows/<id>/activate
  • Deactivate: POST /linker/workflows/<id>/deactivate
  • Copy: POST /linker/workflows/<id>/copy?newName=<newName>
  • Validate: POST /linker/workflows/validate
  • Execute: POST /linker/workflows/<apiKey>/execute

Trigger management (/api/workflows/{workflowId}/triggers, /api/triggers)

  • List: GET /linker/workflows/<workflowId>/triggers
  • Create: POST /linker/workflows/<workflowId>/triggers
  • Get: GET /linker/triggers/<triggerId>
  • Update: PUT /linker/triggers/<triggerId>
  • Delete: DELETE /linker/triggers/<triggerId>
  • View event inbox: GET /linker/triggers/<triggerId>/events?page=&size=
  • Webhook trigger: POST /linker/triggers/webhook/<triggerId>

Trigger object structure (TriggerDef)

Common fields (see backend TriggerDefEntity and TriggerController.TriggerDefDetail):

  • id: trigger ID
  • type: trigger type enum:
    • WEBHOOK: triggered via HTTP webhook
    • CRON: scheduled Cron-based trigger
    • MQ: message-queue trigger
  • name: name
  • enabled: whether enabled
  • workflowApiKey: bound workflow apiKey
  • inputTemplate: input template for mapping trigger events to workflow Start inputs

Webhook-specific fields:

  • webhookSignatureHeader: signature header name (default X-Signature)
  • webhookTimestampHeader: timestamp header name (default X-Timestamp)
  • webhookMaxSkewSeconds: allowed time skew in seconds (default ~300s)
  • webhookHasSecret: whether a webhook secret is configured (read-only, for UI)

Cron-specific fields:

  • cronExpression: Cron expression (for example 0 */5 * * * ?)
  • cronTimezone: timezone (for example Asia/Shanghai)
  • cronNextFireAt: next scheduled fire time (read-only, calculated by scheduler)

MQ-specific fields:

  • mqDriver: message driver type (for example kafka, rabbitmq, implementation-dependent)
  • mqQueue: queue/topic name

Audit fields:

  • createdAt: creation time
  • updatedAt: last updated time

Create and update Trigger

  • Create: POST /linker/workflows/<workflowId>/triggers binds a trigger to the given workflow.
  • Update: PUT /linker/triggers/<triggerId> partially updates fields (only fields in request body are changed).

Type-specific update behaviors:

  • For WEBHOOK:
    • You can update webhookSecret, webhookSignatureHeader, webhookTimestampHeader, webhookMaxSkewSeconds.
  • For CRON:
    • Updating cronExpression or cronTimezone resets cronNextFireAt to be recalculated by the scheduler.
  • For MQ:
    • You can update mqDriver, mqQueue.

Example: update a Webhook trigger’s signing config:

curl -X PUT "${API_BASE}/linker/triggers/<triggerId>" \
-H "Authorization: Bearer ${TOKEN}" \
-H "X-Tenant-Id: <tenantId>" \
-H "Content-Type: application/json" \
-d '{
"name": "Order Webhook",
"enabled": true,
"webhookSecret": "my-signing-secret",
"webhookSignatureHeader": "X-Signature",
"webhookTimestampHeader": "X-Timestamp",
"webhookMaxSkewSeconds": 300
}'

Webhook triggering and signature verification

Trigger endpoint:

  • POST /linker/triggers/webhook/<triggerId>

Request body:

  • arbitrary payload, most commonly application/json

Signature and security (see TriggerController#webhook and verifyWebhookSignature):

  • If webhookSecret is not configured, signature is not verified (for internal/test environments).
  • If webhookSecret is configured:
    • Timestamp header: default X-Timestamp (or configured webhookTimestampHeader)

    • Signature header: default X-Signature (or configured webhookSignatureHeader)

    • Timestamp is a Unix second timestamp (Instant.now().getEpochSecond()).

    • Allowed skew is webhookMaxSkewSeconds; requests beyond that are rejected.

    • Signed payload:

      <timestamp>.<body-as-utf8-string>
    • Signature: HMAC-SHA256(secret, signedPayload) encoded as lowercase hex.

    • Request header supports a prefix form sha256=<hex>; the prefix is stripped before comparison.

    • Constant-time comparison is used to avoid timing attacks.

Client pseudo-code:

TIMESTAMP=$(date +%s)
BODY='{"event":"order.created","id":"evt_123","amount":100}'
SIGNED_PAYLOAD="${TIMESTAMP}.${BODY}"
SIGNATURE_HEX=$(echo -n "$SIGNED_PAYLOAD" | \
openssl dgst -sha256 -hmac "my-signing-secret" | \
sed 's/^.* //')

curl -X POST "${API_BASE}/linker/triggers/webhook/<triggerId>" \
-H "Content-Type: application/json" \
-H "X-Timestamp: ${TIMESTAMP}" \
-H "X-Signature: sha256=${SIGNATURE_HEX}" \
-d "$BODY"

Event idempotency and EventKey

The webhook event idempotency key eventKey is determined (see resolveEventKey) by:

  • Prefer headers in this order:
    • X-Event-Id
    • X-Request-Id
    • X-Idempotency-Key
  • The first non-empty value becomes eventKey; if none exist, eventKey is the SHA-256 hash of the request body.

Within the same Trigger, events sharing the same eventKey are treated as duplicates and return duplicate = true, avoiding double-processing.

Webhook payload parsing

Payload parsing (see parsePayload):

  • If body is empty, the trigger sees {} (empty object).

  • If Content-Type contains application/json and JSON parsing succeeds:

    • payload is that JSON object and passed through to the workflow.
  • Otherwise:

    {
    "raw": "<UTF-8 decoded text>",
    "base64": "<Base64 of raw bytes>"
    }

Webhook response structure

Webhook triggers return:

{
"success": true,
"data": {
"accepted": true,
"duplicate": false,
"eventInboxId": "inbox_123",
"workflowExecutionId": "exec_456"
}
}
  • accepted: whether the event was written to the inbox and entered scheduling.
  • duplicate: whether an eventKey duplicate was detected.
  • eventInboxId: ID of event inbox record.
  • workflowExecutionId: workflow execution ID (if started).

Event inbox (TriggerEventInbox)

View events for a given Trigger:

  • GET /linker/triggers/<triggerId>/events?page=&size=

Response fields (see TriggerEventSummary):

  • items: event list, each with:
    • id: event ID
    • eventKey: idempotency key
    • status: processing status (PENDING / RUNNING / DONE / FAILED, etc.)
    • attempts: retry count
    • workflowExecutionId: bound workflow execution ID
    • lastError: last error message (if any)
    • payloadHash: payload hash (for troubleshooting)
    • createdAt / updatedAt: timestamps
  • page: current page (starting from 0)
  • size: page size
  • total: total count

Task execution and human approvals (/api/task)

  • Run: POST /linker/task/run
  • Resume: POST /linker/task/resume
  • Pending human tasks: GET /linker/task/human/pending
  • Complete approval: POST /linker/task/human/<taskId>/complete
  • Report: GET /linker/task/report?taskID=<executionId>
  • Execution list: GET /linker/task/executions?tenantId=&workflowId=&status=&userId=&startTime=&endTime=&page=&size=

End-to-end execution flow

  1. Linker admin console (visual) creates and publishes workflows.
  2. Configure Triggers (Webhook / Cron / MQ) for workflows.
  3. External systems call gateway /linker/... endpoints to trigger workflows.
  4. Use /linker/task/human/... APIs to drive approval nodes.
  5. Use /linker/task/report and /linker/task/executions for runtime monitoring and troubleshooting.