Back to developers
Cookbook

Working recipes,
copy and adapt.

Six end-to-end recipes built on the Vyrable SDK. Schedule a week of posts, bulk-import a CSV, score-and-rewrite low-citability content, watch a competitor RSS, auto-publish the highest-scoring variants, build a weekly newsletter — all working code you can drop in and adapt.

  1. 01
    Schedule a week of posts in advance
  2. 02
    Bulk-import ideas from a CSV
  3. 03
    Score then regenerate low-citability content
  4. 04
    Capture competitor articles into the idea backlog
  5. 05
    Auto-publish anything that scores ≥ 80
  6. 06
    Build a weekly newsletter from the week's published content
01

Schedule a week of posts in advance

Generate seven posts (one per day, Tuesday morning local time) for next week, then schedule each.

TypeScript/sdk/vyrable.ts
import { VyrableClient } from "./vyrable";

const vy = new VyrableClient({ apiKey: process.env.VYRABLE_API_KEY! });

const topics = [
  "AI search citability fundamentals",
  "Persona-aware content rewriting",
  "Mid-week LinkedIn engagement patterns",
  "Why bigrams beat keyword-stuffing",
  "Weekly newsletter pitfalls",
  "Cross-platform variant authoring",
  "Content recycling without sounding stale",
];

const { data: personas } = await vy.listPersonas();
const personaId = personas[0].id;

// Find next Tuesday at 09:00 in the runtime timezone.
function nextTuesday(at: number): Date {
  const d = new Date();
  d.setUTCHours(at, 0, 0, 0);
  const offset = (2 - d.getUTCDay() + 7) % 7 || 7;
  d.setUTCDate(d.getUTCDate() + offset);
  return d;
}

const start = nextTuesday(9);

for (let i = 0; i < topics.length; i++) {
  const { data: queued } = await vy.generateContent({
    personaId,
    topic: topics[i],
    contentType: "POST",
  });

  // Wait for the multi-agent pipeline to finish (30–90 s typical).
  // In production poll vy.getContent(queued.id) until status === "APPROVED".
  await new Promise((r) => setTimeout(r, 60_000));

  const when = new Date(start);
  when.setUTCDate(when.getUTCDate() + i);
  await vy.scheduleContent(queued.id, when);
  console.log(`Scheduled #${i + 1} for ${when.toISOString()}`);
}

Note: In production, poll getContent until status === "APPROVED" instead of sleeping a fixed 60 seconds.

02

Bulk-import ideas from a CSV

Take a CSV of {title, sourceUrl, tags} rows and push every row into Mission Control.

import csv, os
from vyrable import VyrableClient

vy = VyrableClient(api_key=os.environ["VYRABLE_API_KEY"])

with open("ideas.csv", newline="", encoding="utf-8") as f:
    rows = list(csv.DictReader(f))

for row in rows:
    tags = [t.strip() for t in row.get("tags", "").split(",") if t.strip()]
    out = vy.capture_idea(
        title=row["title"],
        source_url=row.get("sourceUrl") or None,
        tags=tags or None,
    )
    print("captured", out["data"]["id"], "-", row["title"][:60])
03

Score then regenerate low-citability content

Find every PUBLISHED piece below a citability threshold and queue a fresh generation pass for the same topic.

TypeScript/sdk/vyrable.ts
import { VyrableClient } from "./vyrable";
import { analyseAiCitability } from "./ai-citability-lite";
// Drop https://vyrable.ai/tools/citability/ai-citability-lite.ts
// next to vyrable.ts if you want the heuristic locally too.

const vy = new VyrableClient({ apiKey: process.env.VYRABLE_API_KEY! });

const { data: published } = await vy.listContent({
  status: "PUBLISHED",
  limit: 100,
});

for (const summary of published) {
  const { data: full } = await vy.getContent(summary.id);
  const score = analyseAiCitability(full.body).overall;
  if (score >= 60) continue;

  // Re-queue the same topic; the pipeline will produce a stronger
  // variant, then APPROVE/PUBLISH on its own depending on org config.
  await vy.generateContent({
    personaId: full.persona.id,
    topic: full.topic,
    brief: `Rewrite for higher AI-citability. Original score: ${score}`,
  });
  console.log(`requeued ${full.id} (score was ${score})`);
}
04

Capture competitor articles into the idea backlog

Watch an RSS feed for new entries; for each one, drop a Mission Control idea with the source URL preserved.

TypeScript/sdk/vyrable.ts
import { VyrableClient } from "./vyrable";
import { XMLParser } from "fast-xml-parser";

const vy = new VyrableClient({ apiKey: process.env.VYRABLE_API_KEY! });
const parser = new XMLParser({ ignoreAttributes: false });

async function pollFeed(feedUrl: string, sourceName: string) {
  const xml = await fetch(feedUrl).then((r) => r.text());
  const feed = parser.parse(xml);
  const items = feed?.rss?.channel?.item ?? feed?.feed?.entry ?? [];

  // Only capture entries from the last hour (run this on a cron).
  const cutoff = Date.now() - 60 * 60 * 1000;
  for (const item of Array.isArray(items) ? items : [items]) {
    const pubDate = new Date(item.pubDate ?? item.published ?? 0).getTime();
    if (pubDate < cutoff) continue;

    await vy.captureIdea({
      title: String(item.title?._ ?? item.title).slice(0, 280),
      sourceUrl: item.link?.["@_href"] ?? item.link,
      sourceName,
      tags: ["competitor-feed", sourceName.toLowerCase()],
    });
  }
}

await pollFeed("https://example.com/blog/feed.xml", "Example Inc");
05

Auto-publish anything that scores ≥ 80

Walk through APPROVED rows, fetch the variant scores, and publish anything whose winning variant scored ≥ 80.

TypeScript/sdk/vyrable.ts
import { VyrableClient } from "./vyrable";

const vy = new VyrableClient({ apiKey: process.env.VYRABLE_API_KEY! });

const { data: ready } = await vy.listContent({ status: "APPROVED", limit: 50 });

for (const summary of ready) {
  const { data: full } = await vy.getContent(summary.id);
  const winner = full.variants.find((v) => v.isWinner);
  const score = winner?.overallScore ?? 0;
  if (score < 80) continue;

  try {
    const result = await vy.publishContent(full.id);
    console.log(
      "published",
      full.id,
      "→",
      result.data.succeededPlatforms.join(", "),
    );
  } catch (err) {
    console.error("publish failed", full.id, err);
  }
}

Note: Pair this with the Smart Publish v2 per-channel overrides in the dashboard for max effect — generate once, override per platform, auto-publish the winner.

06

Build a weekly newsletter from the week's published content

Once a week, collect everything published in the last 7 days and stitch it into a markdown digest.

import os, datetime
from vyrable import VyrableClient

vy = VyrableClient(api_key=os.environ["VYRABLE_API_KEY"])

# Pull the most recent 100, filter client-side by publishedAt.
res = vy.list_content(status="PUBLISHED", limit=100)
cutoff = datetime.datetime.utcnow() - datetime.timedelta(days=7)

lines = ["# This week on Vyrable", ""]
for c in res["data"]:
    pub = c.get("publishedAt")
    if not pub:
        continue
    if datetime.datetime.fromisoformat(pub.replace("Z", "+00:00")) < cutoff:
        continue
    lines.append(f"## {c['headline'] or c['topic']}")
    lines.append(f"*Persona: {c['persona']['name']}*")
    lines.append("")

print("\n".join(lines))

Have a recipe to suggest?

Email info@vyrable.ai with the workflow you'd like to see covered. We add new recipes whenever a request shows up more than once.