PopcornTeam Admin Docs
Concepts

Flows

Flows are multi-step workflows of agents and integration actions, triggered manually, on a cron, or by an external event.

A flow is a directed graph of steps. Each step runs an agent or an integration action, passes its output downstream, and can branch on conditions.

Triggers

TriggerWhen it fires
ManualA user clicks Run in the flow builder, or hits POST /api/v1/agents/flows/<id>/trigger.
CronA 1-minute ticker Cloud Function reads crons/*, evaluates each schedule with cron-parser, and dispatches due jobs.
EventAn internal or external event matches an eventName registered on the flow.

Anatomy of a flow node

Every flow node is one of:

  • Agent step — runs an agent with a templated task prompt. Variables from upstream nodes are interpolated.
  • Integration action — calls a provider action directly, like gw_gmail_list_messages or slack_post_message. Skips the model and goes straight to the API.
  • Conditional — branches on an expression.
  • Output — final step that posts to a Slack channel, sends an email, or writes a webhook.

When you wire an integration action, set the integrationAction field to the action name as-is, for example gw_gsc_search_analytics. Do not strip or add a provider prefix; the executor resolves tools by action name directly.

Cron-triggered flows in detail

Cron flows live in crons/{id} with the schema:

interface CronJob {
  name: string;
  schedule: string;        // "0 9 * * *"
  timezone?: string;        // IANA, defaults to UTC
  enabled: boolean;
  agentId?: string;         // pin a specific agent…
  routing?: "auto";         // …or let the router pick
  task: string;             // the prompt
  outputs: OutputChannel[]; // where results go (Slack, email, webhook, SMS)
}

Cloud Scheduler does not fire on localhost. To test locally, hit the manual trigger endpoint or use the Run button in the flow builder.

Idempotency and timestamps

Cron lastRunAt is a Firestore Timestamp object at read time, not a string. Always convert with toISOOrFallback() from @/lib/firebase/timestamp — a previous bug treated it as a string, returned undefined, and made every cron fire every minute.

On this page