Docs

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 and scopes.
  • 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).

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. 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