# Draft Blog Post

# Draft a Blog Post

End-to-end recipe for producing a grounded, citation-backed blog post artifact using the `content_writer` agent profile. The agent researches the topic against your business substrate and knowledge base, builds an outline, pauses for your approval, writes the full draft, and finalizes a `platform_artifact` you can fetch, version, and publish.

## Prerequisites

- An API key with scopes `workflows:write` (to start and resume agents) and `assets:read` (to fetch the resulting artifact). See [authentication.md](../authentication.md) and [scopes](../api-reference/scopes.md).
- A business with at least one knowledge base document or some recent data to ground the writing. The agent will still produce a post without these, but grounding quality drops.
- Optional: a `voice_profile_id` if you want the draft to match a saved voice.

Export your key once for the examples below:

```bash
export API_KEY="sk_live_your_key_here"
```

## Steps

### 1. Start the agent

Call `agents.run` with `profile_id='content_writer'`. Pass the task as a natural-language sentence and any structured hints in `input_params`. The `channel` and `content_type` fields steer the profile toward a blog layout.

```bash
curl -sS -X POST "https://app.amdahl.co/api/platform/v1/agents/run" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "profile_id": "content_writer",
    "task": "Write a 900-word blog post on how mid-market SaaS teams evaluate data quality tooling.",
    "input_params": {
      "channel": "blog",
      "content_type": "blog_post",
      "audience": "VP Data at mid-market SaaS"
    },
    "async": true
  }'
```

Expected response:

```json
{
  "success": true,
  "session_id": "as_01HZ...",
  "status": "queued",
  "profile_id": "content_writer",
  "created_at": "2026-04-14T18:22:11.453Z",
  "async": true
}
```

What this tells you: the agent is enqueued. `session_id` is the handle you use for every subsequent call. `status=queued` means no worker has picked it up yet; `running` means the loop has started.

### 2. Poll the status endpoint

Poll every 3 to 5 seconds. A blog post typically takes 60 to 180 seconds depending on how much research the agent decides to do.

```bash
curl -sS "https://app.amdahl.co/api/platform/v1/agents/$SESSION_ID/status" \
  -H "Authorization: Bearer $API_KEY"
```

Expected response while running:

```json
{
  "session_id": "as_01HZ...",
  "status": "running",
  "turn_count": 6,
  "current_tool": "knowledge_base.search",
  "updated_at": "2026-04-14T18:23:08.019Z"
}
```

What this tells you: the agent is alive and actively calling tools. If `turn_count` climbs without `updated_at` advancing, something is stuck; wait out the 60-second inactivity budget then treat as stalled.

### 3. Wait for outline approval

The `content_writer` profile produces an outline before writing. It pauses the session with `status='awaiting_input'` and sets `pending_input_type='outline_approval'`. Keep polling until you see this.

```json
{
  "session_id": "as_01HZ...",
  "status": "awaiting_input",
  "pending_input_type": "outline_approval",
  "pending_input_payload": {
    "outline": {
      "title": "How mid-market SaaS teams actually evaluate data quality tools",
      "sections": [
        { "heading": "Why this matters now", "bullets": ["..."] },
        { "heading": "The three real buying criteria", "bullets": ["..."] }
      ]
    }
  }
}
```

What this tells you: the agent is ready for your sign-off. Do not resume until you have reviewed `pending_input_payload.outline`. If the outline is wrong, send `approved: false` with a revision note.

### 4. Approve the outline

```bash
curl -sS -X POST "https://app.amdahl.co/api/platform/v1/agents/$SESSION_ID/resume" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "input": { "approved": true }
  }'
```

Expected response:

```json
{
  "success": true,
  "session_id": "as_01HZ...",
  "status": "running"
}
```

To reject and request a rewrite instead:

```json
{
  "input": { "approved": false, "revision_note": "Tighten to 3 sections, drop the history intro." }
}
```

### 5. Poll until complete

Same endpoint as step 2. Terminal statuses are `complete`, `failed`, and `canceled`. A successful run returns:

```json
{
  "session_id": "as_01HZ...",
  "status": "complete",
  "result_summary": "Drafted a 912-word blog post with 6 citations.",
  "result_artifact_id": "art_01HZ...",
  "turn_count": 14,
  "updated_at": "2026-04-14T18:25:41.221Z"
}
```

What this tells you: the artifact is saved. Stop polling. Grab `result_artifact_id`.

### 6. Fetch the artifact

```bash
curl -sS "https://app.amdahl.co/api/platform/v1/artifacts/$ARTIFACT_ID" \
  -H "Authorization: Bearer $API_KEY"
```

Expected response (shortened):

```json
{
  "id": "art_01HZ...",
  "artifact_type": "blog_post",
  "status": "draft",
  "title": "How mid-market SaaS teams actually evaluate data quality tools",
  "content_markdown": "# How mid-market SaaS teams...\n\n...",
  "content_json": {
    "sections": [{ "heading": "...", "body_markdown": "..." }],
    "sources": [{ "doc_id": "kb_...", "title": "...", "url": "..." }]
  },
  "tags": ["blog", "data-quality"],
  "version": 1,
  "created_at": "2026-04-14T18:25:40.992Z"
}
```

What this tells you: `content_markdown` is the post body. `content_json.sources` is the citation list the agent grounded claims against. `status=draft` means no human has marked it approved yet.

### 7. Mark the artifact as saved (optional)

When your team signs off, flip the lifecycle status. This also fires the `artifact.status_changed` webhook event, which you can subscribe to if you want downstream automation (see [react-to-webhook.md](./react-to-webhook.md)).

```bash
curl -sS -X PATCH "https://app.amdahl.co/api/platform/v1/artifacts/$ARTIFACT_ID" \
  -H "Authorization: Bearer $API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "status": "saved" }'
```

Expected response:

```json
{
  "id": "art_01HZ...",
  "status": "saved",
  "version": 2,
  "updated_at": "2026-04-14T18:31:05.117Z"
}
```

What this tells you: the artifact is locked to v2. Any further content edit creates v3 and archives v2 in `platform_artifact_versions`.

## Common issues

- **Agent never leaves `queued`**: worker capacity is exhausted. Check [rate-limits.md](../rate-limits.md). Retry after 30 seconds.
- **`status=failed` with `error.code='tool_error'`**: inspect `last_error_message` in the status response. Most often a knowledge base is empty. Retry with `input_params.reference_doc_only=false` or provide `reference_document_ids`.
- **Outline never arrives**: the profile skips outline approval when `input_params.skip_outline_approval=true`. Check that you did not set this flag.
- **Artifact missing citations**: the agent could not ground claims. Either your substrate is thin or the topic is outside its scope. Re-run with `input_params.reference_document_ids` pointing at curated docs.
- **Resume rejected with `invalid_state`**: the session already moved past `awaiting_input`, usually because of a second resume call. Check `status` before resuming.

## See also

- [agents.md](../agents.md) for the full agent lifecycle.
- [api-reference/tools/workflows.md](../api-reference/tools/workflows.md) for the `agents.*` and `content.*` tool schemas.
- [api-reference/tools/assets.md](../api-reference/tools/assets.md) for `artifacts.*` endpoints.
- [recipes/run-research.md](./run-research.md) for a companion research-only run.
- [recipes/react-to-webhook.md](./react-to-webhook.md) for automating downstream work when a draft becomes `saved`.
