# Estimate and run a Workflow Starter

> Price exact starter inputs, preserve the returned versions, and create one idempotent workflow run.

Canonical: https://entirefeed.com/docs/workflows/estimate-and-run

Estimate the exact `inputs`, `starter_version`, and optional `account_id` you intend to run. Estimation creates no media. A run is accepted only when its estimate and starter versions are still current and every paid step has a known cost.

Creation is the paid action. Send the estimate response's versions with the same inputs and a stable `Idempotency-Key`. Playground use is optional; direct API integrations follow the same contract.

After creation, use the returned `links.self` URL as described in [Monitor, cancel, and retry](/docs/workflows/monitor-cancel-retry).

## Estimate the exact inputs

Estimate the exact intended inputs and current starter version. This request creates no media:

### cURL

```bash
curl https://api.entirefeed.com/v1/workflow-starters/text-to-video-basic/estimate \
  -X POST \
  -H "Authorization: Bearer $ENTIREFEED_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "inputs": {
    "video_idea": "A slow product reveal with soft daylight and a clean studio background"
  },
  "starter_version": "st_0123456789abcdef01234567"
}'
```

### TypeScript

```typescript
const response = await fetch("https://api.entirefeed.com/v1/workflow-starters/text-to-video-basic/estimate", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.ENTIREFEED_API_KEY}`,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "inputs": {
      "video_idea": "A slow product reveal with soft daylight and a clean studio background"
    },
    "starter_version": "st_0123456789abcdef01234567"
  }),
});

if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
```

### Python

```python
import os
import requests

response = requests.request(
    "POST",
    "https://api.entirefeed.com/v1/workflow-starters/text-to-video-basic/estimate",
    headers={"Authorization": f"Bearer {os.environ['ENTIREFEED_API_KEY']}", "Content-Type": "application/json"},
    json={
      "inputs": {
        "video_idea": "A slow product reveal with soft daylight and a clean studio background",
      },
      "starter_version": "st_0123456789abcdef01234567",
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
```

```json
{
  "object": "workflow_starter_estimate",
  "starter_slug": "text-to-video-basic",
  "starter_version": "st_0123456789abcdef01234567",
  "estimated_cost": {
    "amount_usd": "1.53"
  },
  "estimated_micro_usd": 1530000,
  "line_items": [
    {
      "label": "Video generation",
      "amount_usd": "1.53",
      "confidence": "exact",
      "reason": "Calculated from the current starter inputs."
    }
  ],
  "unknown_costs": [],
  "estimate_version": "estimate_v1"
}
```

Use the returned amount as current pricing for this request; example amounts are illustrative. Continue only when `unknown_costs` is empty.

## Create one intended run

Creation is paid and requires available balance. Send the same inputs, `starter_version`, and `estimate_version`, plus one `Idempotency-Key` for this intended run:

### cURL

```bash
curl https://api.entirefeed.com/v1/workflow-starters/text-to-video-basic/runs \
  -X POST \
  -H "Authorization: Bearer $ENTIREFEED_API_KEY" \
  -H "Idempotency-Key: launch-video-042" \
  -H "Content-Type: application/json" \
  -d '{
  "inputs": {
    "video_idea": "A slow product reveal with soft daylight and a clean studio background"
  },
  "starter_version": "st_0123456789abcdef01234567",
  "estimate_version": "estimate_v1",
  "metadata": {
    "customer_job_id": "launch-video-042"
  }
}'
```

### TypeScript

```typescript
const response = await fetch("https://api.entirefeed.com/v1/workflow-starters/text-to-video-basic/runs", {
  method: "POST",
  headers: {
    "Authorization": `Bearer ${process.env.ENTIREFEED_API_KEY}`,
    "Idempotency-Key": "launch-video-042",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    "inputs": {
      "video_idea": "A slow product reveal with soft daylight and a clean studio background"
    },
    "starter_version": "st_0123456789abcdef01234567",
    "estimate_version": "estimate_v1",
    "metadata": {
      "customer_job_id": "launch-video-042"
    }
  }),
});

if (!response.ok) throw new Error(await response.text());
console.log(await response.json());
```

### Python

```python
import os
import requests

response = requests.request(
    "POST",
    "https://api.entirefeed.com/v1/workflow-starters/text-to-video-basic/runs",
    headers={"Authorization": f"Bearer {os.environ['ENTIREFEED_API_KEY']}", "Idempotency-Key": "launch-video-042", "Content-Type": "application/json"},
    json={
      "inputs": {
        "video_idea": "A slow product reveal with soft daylight and a clean studio background",
      },
      "starter_version": "st_0123456789abcdef01234567",
      "estimate_version": "estimate_v1",
      "metadata": {
        "customer_job_id": "launch-video-042",
      },
    },
    timeout=30,
)
response.raise_for_status()
print(response.json())
```

```json
{
  "id": "wrun_0123456789abcdef0123456789abcdef",
  "object": "workflow_run",
  "status": "queued",
  "starter_slug": "text-to-video-basic",
  "starter_version": "st_0123456789abcdef01234567",
  "created_at": "2026-07-14T08:30:00Z",
  "estimated_cost": {
    "amount_usd": "1.53"
  },
  "metadata": {
    "customer_job_id": "launch-video-042"
  },
  "links": {
    "self": "https://api.entirefeed.com/v1/workflow-runs/wrun_0123456789abcdef0123456789abcdef"
  }
}
```

## Replay uncertain requests safely

After a timeout or dropped response, repeat the same create request with the same key. The API returns the original `wrun_...` resource instead of creating duplicate work. Changed inputs or metadata require a new key and a new estimate.

## Handle preflight failures

- `409 workflow_estimate_required`: Estimate the workflow starter before running it. Estimate before creating the run.
- `409 workflow_estimate_stale`: Workflow estimate is stale. Estimate again before running. Estimate the same intended inputs again.
- `409 starter_version_stale`: Workflow starter version is stale. Read the starter, revalidate, and estimate again.
- `409 idempotency_conflict`: Idempotency-Key was reused with a different request body. Use the original body or a new intent key.
- `422 unknown_workflow_cost`: Workflow contains paid steps whose cost cannot be estimated. Stop; the run cannot reserve a known amount.

An unpriceable run is rejected before creation:

```json
{
  "error": {
    "type": "unknown_workflow_cost",
    "code": "unknown_workflow_cost",
    "message": "Workflow contains paid steps whose cost cannot be estimated.",
    "request_id": "req_0123456789abcdef"
  }
}
```
