For AI agents: a documentation index is available at https://docs.plungeai.com/llms.txt. Append .md to any page URL, or send Accept: text/markdown, to get markdown. Setup instructions for agents are at https://docs.plungeai.com/agents.md. Execution planes take an ozk_ key; the models plane takes an sk-ocean- key.

Documentation Index: fetch the complete documentation index at /llms.txt. Use this file to discover all available pages before exploring further.

A campaign claims a list of items (a data-table query, an agent's lister output, an uploaded CSV, or an inline list), runs each item through a CNL pipeline with per-item retries, and repeats on a cadence until the list ("cycle") is drained — then either re-cycles (refill/reset) or hands off to another campaign (then). It is PlungeAI's answer to "process these 280 rows every Monday" or "watch these 50 URLs forever."

Plain fact about the surface: there is no dedicated public One API route or MCP tool for managing a campaign today (https://api.plungeai.com and https://mcp.plungeai.com/v1 expose neither) — a campaign is authored visually in Ocean Studio via the plungeai-campaign-agent kind (CNL YAML + a campaign-config fenced block), and its runtime primitives are plain CNL agent: data-table-agent operations you can also call directly from a workflow you build yourself. This skill documents what exists; for the authoring UI itself use plungeai-campaign-agent.

Discover the pieces first

A campaign's pipeline is ordinary CNL, so discover its agents the normal way — never guess an id: plungeai_list_agents {search: "<what the item-processing step needs>"} (REST: GET /v1/discovery/search?kind=agents&q=…). data-table-agent itself is a registry agent id — confirm it live the same way.

The campaign-config block

A fenced ```campaign-config block sits after the CNL YAML; Studio parses it to open the ledger and create the scheduler job.

FieldRequiredMeaning
listyesWhere items come from: table: (a data-table project+table), agent: (a lister task whose JSON array feeds items_from), csv: true (uploaded rows), or inline: [key, …].
cycleyesCalendar bucket: once | hourly | daily | weekly | monthly | continuous.
cycle_startwhen re-listingrefill (add new list rows, keep prior) or reset (fresh cycle from the list).
scheduleyesCron for the run cadence, e.g. "*/5 9 * * 1" — the scheduler re-fires until the cycle drains (plungeai-scheduling).
max_attemptsyesPer-item retry cap before an item is marked failed (integer ≥ 1; the ledger column defaults to 2).
thennoCampaign id to hand the baton to when this one's cycle completes.
delivernoChannels for the cycle-complete notice (below).
localnotrue runs items on the user's own computer.
max_itemsnoHard cap on total items claimed across the cycle.
task_typesnoPer-row task/worker overrides.
learnnoEnables the ledger's recall/knowledge lane.

The CNL shape — claim, batch, complete

workflow:
  name: "RH price check"
  tasks:
    - type: task
      id: claim
      agent: data-table-agent
      operation: campaignClaim
      campaign_id: "<written by Studio at save>"
      batch_size: 40
    - type: batch
      id: work
      items_from: claim
      concurrency: 5
      ledger: campaign
      tasks:
        - type: task
          id: one
          agent: price-collector-v2-agent
          operation: collect_single
          product_name: "{item.key}"
          week_date: "{item.cycle}"

type: batch with ledger: campaign is what wires the batch's per-item outcome back into the campaign ledger automatically (complete/fail bookkeeping) — see plungeai-workflows for the general batch task type. An item's work can be a plain task (as above) or an open-ended type: harness mission (plungeai-missions) when the per-item work needs judgment, not a fixed call.

The ledger primitives (agent: data-table-agent operations)

Every operation is owner-scoped (owner_uuid — resolved from your account, not a field you pass by hand):

OperationPurpose
campaignBeginCreate/upsert the campaign header at save time (list source, cycle kind, batch/concurrency/max_attempts, optional seed items).
campaignClaimLease up to batch_size pending items for this run (run_id); opens a new cycle if the prior one drained and cycle_start allows it. Refuses to lease from a non-active campaign.
campaignCompleteMark one leased item done with its result — only a currently-leased row transitions (a stale duplicate is a safe no-op).
campaignCompleteHumanComplete a worker_kind: "human" row directly — human rows are never leased, so this is the only way one closes out.
campaignFailRecord a failed attempt; the item returns to pending until max_attempts, then flips to failed.
campaignReleaseUn-lease claimed-but-unfinished rows (e.g. a crashed run) back to pending.
campaignStatusRead the header + per-status item counts.
campaignAddItemsAdd items to the current (or a named) cycle; refuses past max_items.
campaignRetryFailedRe-queue failed items (optionally a specific key list) back to pending.
campaignSetStatusPause/resume/mark done; pausing stamps paused_by (default user:<owner>; the scheduler's own zero-progress breaker stamps auto:zero-progress).
campaignDeleteSoft-delete (deleted_at + status='done'); releases held leases; history stays queryable.
campaignTasks / campaignUpsertTasks / campaignSetTaskActiveRead/edit the campaign's editable task-row list (the Task-app grid).
campaignResults / campaignPivotRecent per-item results (optionally rolled up per cycle) / a tasks-×-runs reporting grid.
chainStepThe zero-AI "forever campaign" unit of work: bump a lane counter once per cycle (idempotent under at-least-once re-claims) and log a per-run row.
counterStepA pure per-cycle counter (prev + step), for the simplest possible repeating tally.

Full parameter shapes live in core/core-data-table-agent/campaign-ledger.ts, chain-step.ts, counter-step.ts — read those before hand-building a pipeline around one of these operations; nothing here is invented beyond what that code implements.

Cadence — two ways a campaign keeps going

  1. Cron schedule — an ordinary recurring scheduler job re-fires the pipeline until the cycle drains (the Forever Campaign ran on */5 * * * *). This is the normal path when campaign-config.schedule is set.
  2. runAgain self-scheduling — a campaign saved without a cron schedule ends its pipeline with agent: scheduler, operation: runAgain, in: "1h" (or another duration). This creates exactly ONE @once job for the workflow and never stacks — an existing pending @once job for the same workflow is re-timed, not duplicated. See plungeai-scheduling for @once/runAgain mechanics and cron/date-token reference in full.

Sizing

Keep batch_size × seconds-per-item ÷ concurrency ≤ 600s — comfortably inside the scheduler's execution window. The defaults (batch_size: 40, concurrency: 5 at ~30s/item ≈ 4 minutes per run) are safe up to roughly 100 items per scheduled run; batch_size/concurrency are hidden authoring knobs, not something you expose to an end operator.

Delivery on cycle-complete (deliver)

  • Fire-and-forget: a delivery failure never fails the run.
  • Length caps per channel: telegram 3800, whatsapp 3800, discord 1900, slack 3800, email 100000, inapp 2000 characters — keep output well under the smallest cap you use.
  • telegram / whatsapp targets must be paired to the owner first (a pairing code from Studio, /pair <code> from that chat); discord requires any active pairing. Unpaired targets are skipped.
  • email sends from the platform address; a bare target means the owner's address — any other address needs that holder's prior confirmation, else it's skipped silently.
  • slack: a bare target DMs the owner; a specific chat_id/to needs the owner to be a connected workspace member.

Gotchas

  • upsertRow is insert-only, not keyed — it does not match matchFields or update in place, and a JSON-string data is not parsed. For an idempotent per-item write use chainStep or explicit top-level col_<name> columns (insertRow/upsertRow substitute {item.*} only into top-level string fields); to update an existing row use updateRows.
  • A partial cycle should never trigger a dependent report via then — chain only full completions to full completions (a data-collection campaign feeding a time-triggered analysis job is the safer pattern than chaining the analysis itself).
  • The zero-progress breaker auto-pauses a campaign after several consecutive completed-runs-that-completed-nothing (an all-failing list) — check campaignStatus/the Task app before assuming a stalled campaign needs a manual nudge.
  • Every ledger operation is scoped by the caller's own owner_uuid — there is no cross-tenant campaign management surface.

Planned: TI-33

Search is not available yet. Until it ships, use the page index or browse the sidebar.

Planned: TI-34

The docs assistant is not available yet. You can hand these docs to your own assistant instead.