Back to docs
Workflow API v1

Route an intent into a governed project workflow.

Services and agents can define deterministic classifier/template contracts, inspect readiness without side effects, invoke once, and consume a strict artifact or signed completion event.

Project scopedDeterministic firstIdempotent invocation

Resolve, preflight, invoke, launch, consume.

Resolution answers which contract would match. Preflight separately proves route, roster, runtime, relay, console, and policy readiness. Neither creates a draft, invocation, or run.

01 Resolve

Deterministic contract match and explanation.

02 Preflight

Read-only launch readiness and typed blockers.

03 Invoke

Persist one request under an idempotency key.

04 Launch

Start or reconcile the governed run.

05 Consume

Poll status/artifact or accept signed delivery.

Create a key for one job, not every job.

Send the API key in X-API-Key. Keep keys and delivery signing secrets on the server. The browser should receive workflow state and artifact data, never stored credentials.

ProfileScopesUse
Read-only resolverworkflow-contracts:readList contracts, resolve intent, and run preflight.
Contract authorworkflow-contracts:read, workflow-contracts:writeCreate and update classifier/template contracts.
Invokerworkflow-contracts:read, workflows:run, workflows:readCreate, launch, retry, and inspect invocations.
Result consumerworkflows:read, workflow-artifacts:readPoll lifecycle and retrieve strict artifacts.
Artifact publisherworkflow-artifacts:writePublish the result owned by an invocation.
Schema authorartifact-schemas:read, artifact-schemas:writeRegister, validate, and deprecate artifact schemas.
Delivery managerworkflow-delivery:manageCreate callbacks and inspect delivery retries.

Keep routing deterministic and versioned.

A project workflow contract combines deterministic classifier rules with one versioned template. Empty classifiers are rejected; a universal fallback must be explicit. Stable template keys and semantic versions let callers pin behavior.

{
  "name": "Market Lens briefing",
  "priority": 100,
  "classifier": {
    "intentContains": ["market lens", "briefing"],
    "requiredCapabilities": ["market_analysis", "macro_research"],
    "metadata": {"source_app": "trading-os", "source_route": "market_lens.run"}
  },
  "template": {
    "key": "market_lens_briefing",
    "version": "1.0.0",
    "required_capabilities": ["market_analysis", "macro_research"],
    "artifact_schema": { "name": "market_lens_briefing", "version": "1.0.0" },
    "guardrails": { "execution_mode": "paper", "human_approval_required": true }
  }
}

Create the contract with an author key. Save the returned contract_id; callers may pin it when they create an invocation.

curl -sS "$MIDFLEET_API/api/v1/projects/$PROJECT_ID/workflow/contracts" \
  -H "X-API-Key: $MIDFLEET_API_KEY" \
  -H "Content-Type: application/json" \
  --data @workflow-contract.json

List active contracts with GET /api/v1/projects/<project-id>/workflow/contracts, inspect one with GET /api/v1/projects/<project-id>/workflow/contracts/<contract-id>, and update it with PATCH on the same URL. Set is_active to false to retire a contract without deleting its audit history. The complete request and response shapes are authoritative in OpenAPI.

Register result schemas separately

Schema authors use POST /api/v1/workspaces/<workspace-id>/artifact-schemas with artifact-schemas:write. Consumers use artifact-schemas:read to inspect a version or validate a payload before publication. Versions are immutable; deprecate an old version instead of changing it in place.

Broad legacy read/write keys do not grant artifact-schema access. Existing automation must rotate to the exact scopes before using these endpoints. Treat artifact-schemas:write as destructive because deprecation has no restore action.

{
  "project_id": "<project-id>",
  "name": "market_lens_briefing",
  "version": "1.0.0",
  "json_schema": {
    "type": "object",
    "required": ["environment", "macro_score", "confidence"],
    "properties": {
      "environment": {"type": "string"},
      "macro_score": {"type": "number"},
      "confidence": {"type": "number", "minimum": 0, "maximum": 1}
    }
  }
}

Resolve and preflight before creating work.

curl -sS "$MIDFLEET_API/api/v1/projects/$PROJECT_ID/workflow/resolve-intent" \
  -H "X-API-Key: $MIDFLEET_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "intent": "Create today market lens briefing",
    "required_capabilities": ["market_analysis", "macro_research"],
    "metadata": {"source_app": "trading-os", "source_route": "market_lens.run"}
  }'
curl -sS "$MIDFLEET_API/api/v1/projects/$PROJECT_ID/workflow/preflight" \
  -H "X-API-Key: $MIDFLEET_API_KEY" \
  -H "Content-Type: application/json" \
  --data '{
    "intent": "Create today market lens briefing",
    "required_capabilities": ["market_analysis", "macro_research"],
    "metadata": {"source_app": "trading-os", "source_route": "market_lens.run"}
  }'

Check route_ready and can_launch separately. Typed blockers distinguish classifier/template configuration, roster ownership, runtime endpoints, spawn policy, and infrastructure health.

One logical request, one invocation.

curl -sS "$MIDFLEET_API/api/v1/projects/$PROJECT_ID/workflow/invocations" \
  -H "X-API-Key: $MIDFLEET_API_KEY" \
  -H "Idempotency-Key: trading-os:market-lens:2026-07-12" \
  -H "X-Correlation-ID: market-lens:2026-07-12" \
  -H "Content-Type: application/json" \
  --data '{
    "intent": "Create today market lens briefing",
    "workflow_contract_id": "<workflow-contract-id>",
    "workflow_template_key": "market_lens_briefing",
    "workflow_template_version": "1.0.0",
    "required_capabilities": ["market_analysis", "macro_research"]
  }'

Reuse the same idempotency key only for the same normalized request. A replay returns the original invocation; changed content returns a conflict. Store the returned invocation_id before launching.

curl -sS -X POST \
  "$MIDFLEET_API/api/v1/projects/$PROJECT_ID/workflow/invocations/$INVOCATION_ID/launch" \
  -H "X-API-Key: $MIDFLEET_API_KEY"

The same contract from TypeScript and Python.

// TypeScript - keep this code on the server
const response = await fetch(`${process.env.MIDFLEET_API}/api/v1/projects/${projectId}/workflow/invocations`, {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-API-Key': process.env.MIDFLEET_API_KEY!,
    'Idempotency-Key': idempotencyKey,
    'X-Correlation-ID': correlationId,
  },
  body: JSON.stringify({ intent, workflow_contract_id: contractId }),
})
if (!response.ok) throw new Error(`Midfleet invocation failed: ${response.status}`)
const { data } = await response.json()
# Python - keep this code on the server
response = httpx.post(
    f"{midfleet_api}/api/v1/projects/{project_id}/workflow/invocations",
    headers={
        "X-API-Key": midfleet_api_key,
        "Idempotency-Key": idempotency_key,
        "X-Correlation-ID": correlation_id,
    },
    json={"intent": intent, "workflow_contract_id": contract_id},
    timeout=20,
)
response.raise_for_status()
invocation = response.json()["data"]

Poll safely or subscribe to signed completion.

Read lifecycle state with workflows:read. After launch, the response includes ordered stage progress, the current stage and owner, the latest stage handoff, and a typed attention signal. Fetch the artifact with workflow-artifacts:read only after the invocation reports an artifact identity and reaches completed.

GET /api/v1/projects/<project-id>/workflow/invocations/<invocation-id>
GET /api/v1/projects/<project-id>/workflow/invocations/<invocation-id>/artifact

Artifact envelopes include stable artifact, project, invocation, run, schema, version, summary, payload, provenance, limitations, and creation identity. Consumers must reject missing or mismatched identity and unsupported schema versions.

waiting_for_artifact is not success: the run is terminal but the required result has not been published, so artifact readback may still return not found. Continue bounded polling or wait for signed completion delivery, then time out and alert.

For callbacks, create a delivery subscription with a server-only HTTPS URL. Midfleet returns the signing secret once. Verify X-Midfleet-Timestamp and X-Midfleet-Signature over the exact body, deduplicate by X-Midfleet-Delivery-ID, and return a non-2xx response to request a retry.

Read the blocker category before retrying.

SignalMeaningAction
matched=falseNo deterministic contract matched.Add explicit rules or intentionally use generic Control fallback.
route_ready=falseClassifier/template selection is incomplete or invalid.Fix project contract configuration; do not spawn agents yet.
can_launch=falseRoute matched but roster, runtime, relay, console, or policy is blocked.Use typed blockers and recommended actions.
409 idempotency conflictThe same key was reused for different content.Restore the original request or use a new logical key.
waiting_for_artifactThe run completed before result publication.Continue bounded polling or wait for signed delivery.
artifact schema mismatchThe result is not safe for this consumer.Keep raw evidence, reject publication, and fix the producer template.

Explicit project routing does not erase generic Control.

When no classifier matches, resolution returns matched=false without creating state. Generic Control can still create a draft, but a project invocation cannot launch until an explicit project contract/template matches. Deterministic rules run first; optional model assistance must be explicitly enabled, governed, budget-capped, audited, and safe for the supplied context.

Workflow contract/template and artifact schema versions are immutable caller-defined identifiers; semantic versions are recommended but not currently enforced. Additive API changes remain within the current semantic public contract version; removals require a deprecation notice of at least 90 days and an entry in the changelog.