> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mavera.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Content Audit → Refresh Pipeline

> Flag stale WordPress posts older than 12 months, generate a prioritized refresh plan with Mavera, and produce updated outlines

### Scenario

Blog content decays — statistics go stale, links break, and SEO rankings drop. You pull every published post with its date and categories, flag anything older than 12 months as stale, send the list to Mavera for a prioritized refresh plan, then generate updated outlines for the top-priority posts.

### Architecture

```mermaid theme={"dark"}
flowchart LR
    A["WordPress GET /wp-json/wp/v2/posts (paginated)"] --> B["Filter stale posts (12+ months)"]
    B --> C["POST /api/v1/mave/chat"]
    C --> D["POST /api/v1/generations"]
    D --> E["Prioritized refresh queue"]
```

### Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests
  from datetime import datetime, timedelta, timezone

  WP, MV = os.environ["WORDPRESS_URL"], os.environ["MAVERA_API_KEY"]
  MB = "https://app.mavera.io/api/v1"
  MH = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
  cutoff = (datetime.now(timezone.utc) - timedelta(days=365)).isoformat()

  posts, page = [], 1
  while True:
      resp = requests.get(f"{WP}/wp-json/wp/v2/posts", params={
          "status": "publish", "per_page": 100, "page": page,
          "_fields": "id,title,date,modified,categories,slug,link"})
      if resp.status_code == 400: break
      resp.raise_for_status()
      batch = resp.json()
      if not batch: break
      posts.extend(batch)
      if page >= int(resp.headers.get("X-WP-TotalPages", 1)): break
      page += 1
  print(f"Total posts: {len(posts)}")

  stale = sorted([p for p in posts if p["modified"] < cutoff], key=lambda p: p["modified"])
  print(f"Stale posts (12+ months): {len(stale)}")

  listing = "\n".join(f"- [{p['title']['rendered']}]({p['link']}) — modified: {p['modified'][:10]}" for p in stale[:40])
  plan_resp = requests.post(f"{MB}/mave/chat", json={"input": [
      {"role": "system", "content": "Content strategist. Prioritize stale posts for refresh by SEO value, evergreen potential, update effort. Numbered list: priority, title, reason, action."},
      {"role": "user", "content": f"Posts not updated in 12+ months:\n\n{listing}\n\nPrioritize top 10."},
  ]}, headers=MH)
  plan_resp.raise_for_status()
  print(f"\n--- Refresh Plan ---\n{plan_resp.json()['choices'][0]['message']['content']}")

  for p in stale[:3]:
      gen = requests.post(f"{MB}/generations", json={
          "prompt": f"Updated outline for:\nTitle: {p['title']['rendered']}\nURL: {p['link']}\nLast updated: {p['modified'][:10]}\n\nProvide: updated title, 5-7 headings, key points, stats to research."
      }, headers=MH)
      gen.raise_for_status()
      print(f"\n--- Outline: {p['title']['rendered']} ---\n{gen.json()['text']}")
  ```

  ```javascript JavaScript theme={"dark"}
  const WP = process.env.WORDPRESS_URL, MV = process.env.MAVERA_API_KEY;
  const MB = "https://app.mavera.io/api/v1";
  const MH = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
  const cutoff = new Date(Date.now() - 365 * 86400000).toISOString();

  (async () => {
    const posts = []; let page = 1;
    while (true) {
      const resp = await fetch(`${WP}/wp-json/wp/v2/posts?status=publish&per_page=100&page=${page}&_fields=id,title,date,modified,categories,slug,link`);
      if (resp.status === 400) break;
      const batch = await resp.json();
      if (!batch.length) break;
      posts.push(...batch);
      const totalPages = parseInt(resp.headers.get("X-WP-TotalPages") || "1");
      if (page >= totalPages) break;
      page++;
    }
    console.log(`Total posts: ${posts.length}`);

    const stale = posts.filter(p => p.modified < cutoff).sort((a, b) => a.modified.localeCompare(b.modified));
    console.log(`Stale posts (12+ months): ${stale.length}`);

    const listing = stale.slice(0, 40).map(p => `- [${p.title.rendered}](${p.link}) — modified: ${p.modified.slice(0, 10)}`).join("\n");
    const planResp = await fetch(`${MB}/mave/chat`, {
      method: "POST", headers: MH,
      body: JSON.stringify({ input: [
        { role: "system", content: "Content strategist. Prioritize stale posts for refresh by SEO value, evergreen potential, update effort. Return numbered list: priority, title, reason, action." },
        { role: "user", content: `Stale posts (12+ months):\n\n${listing}\n\nPrioritize top 10.` },
      ] }),
    });
    const plan = (await planResp.json()).output[0].content[0].text;
    console.log(`\n--- Refresh Plan ---\n${plan}`);

    for (const p of stale.slice(0, 3)) {
      const gen = await fetch(`${MB}/generations`, {
        method: "POST", headers: MH,
        body: JSON.stringify({ prompt: `Updated outline for:\nTitle: ${p.title.rendered}\nURL: ${p.link}\nLast updated: ${p.modified.slice(0, 10)}\n\nProvide: updated title, 5-7 headings, key points, stats to research.` }),
      });
      console.log(`\n--- Outline: ${p.title.rendered} ---\n${(await gen.json()).text}`);
    }
  })();
  ```
</CodeGroup>

### Example Output

```text theme={"dark"}
Total posts: 247 | Stale (12+ months): 83

--- Refresh Plan ---
1. HIGH — "Remote Work Statistics 2024" — modified 2024-11-15
   Action: Update all stats to 2026 sources, add hybrid work section.
2. HIGH — "Complete Guide to Email Marketing" — modified 2024-08-22
   Action: Add AI-assisted email section, update platform comparisons.
3. MEDIUM — "Best Project Management Tools" — modified 2024-06-10
   Action: Re-evaluate tool list, update screenshots and pricing.
```

### Error Handling

<AccordionGroup>
  <Accordion title="Pagination returns 400 on last page">
    WordPress returns HTTP 400 when you request a page beyond `X-WP-TotalPages`. The code breaks on status 400. Alternatively, read `X-WP-TotalPages` from the first response and stop before exceeding it.
  </Accordion>

  <Accordion title="Chat completion context too large">
    If your site has hundreds of stale posts, the listing may exceed the context window. The code caps at 40 posts. For larger audits, split by category or date range and merge the top results.
  </Accordion>
</AccordionGroup>
