Skip to content

Preset catalog

A preset is a named, reusable step chain. Wrap one in a schedule to run it on cron.

The registered set is the JobPresetName union in packages/platform-domain/src/jobs/entities/JobPreset.ts — ten presets, listed below. That file is the source of truth; if this page disagrees with it, it’s wrong.

Almost every step registers unconditionally at boot, so presets are effectively always status: 'ready'. What actually varies per user is the connector each chain needs — credentials live on connector rows (gmail://, llm://, post://…), not in env vars. The “Requires” column below names those connectors; where a preset genuinely still depends on process env, the variable is named explicitly.

PresetPipelineRequires
veilleToBoardread_gmailread_urlenrichdispatch(dashboard://)tag — enriched links become kanban todo cardsconnected gmail:// + llm:// connectors
socialVeilleread_gmailread_urlenrichdispatch(post://…@x.com)tag — one X post draft per link, no auto-postingconnected gmail:// + llm://, plus a connected X account (post://…@x.com)
gmailTriageread_gmailtriagetag — an LLM decides whether each thread needs follow-up and drafts a kanban Taskconnected gmail:// + llm://; optionally a registered Skill to steer the verdict
getSuppliersInvoicesread_gmailcollect_invoicestag — deterministic rules decide first, the LLM is consulted only for mail they can’t call; each hit becomes a link card tagged from:invoices that /accounting listsconnected gmail://; llm:// only if you leave the tiebreak on

All four take a raw extraQuery input (Gmail search syntax — label:Triage, from:someone@x.com, subject:invoice, …) as the whole inclusion filter, plus a processedLabel whose -label:<…> exclusion is appended automatically. That exclusion is what makes re-runs idempotent, and each preset defaults to a distinct processed label (board / social / triaged / invoices) so two presets can watch the same source label without stealing each other’s messages. extraQuery is required with no default on the first three — a schedule can’t be created without deliberately scoping it, since an unset filter would scan the whole mailbox. getSuppliersInvoices is the exception: an invoice sweep has an obvious starting scope, so it ships newer_than:90d (has:attachment OR facture OR invoice OR receipt) as a default. Narrow it once you know your own senders.

Why collect_invoices and not triage. triage is catalogued mode: 'ai', so it credit-gates the entire step and calls the model on every message — which would make the common, free case (a PDF from a known supplier) depend on a balance it never needed. The supplier allowlist and keyword rules settle that case outright; turning llmTiebreak off means the job never spends credits at all. Nothing is archived either: a card carries links (the supplier’s own URL, plus always a Gmail permalink), because storing the PDF would require a Drive write scope and re-consent from every user.

Drafts are owner-scoped (userId stamped at creation). Publishing is worker-owned, atomic-claimed, and idempotent.

PresetPipelineRequires
calendarToTaskscalendar_to_tasks — upcoming Google Calendar events (now → +daysAhead, every accessible calendar) become one owner-scoped kanban Task each, idempotent on the event ida gmail:// connector whose token carries the calendar.readonly scope
captureCommentscapture_comments — comments on the campaign posts you list (LinkedIn org page + Facebook Page) become one sales-lead Task per commenter who wrote the trigger word (default oui), idempotent on the comment idat least one connected LinkedIn / Facebook Page account — with none connectable the step isn’t registered and the preset fails fast
PresetPipelineRequires
agentResearchagent — hands a goal plus a read-mostly toolset (scrape + create_task) to a reasoning loop that runs until done or maxStepsan llm:// connector on an anthropic or openrouter provider — the tool loop is unavailable on a Cline-only provider
implementTaskimplement_task — picks up in-progress Tasks carrying the trigger tag (default agent:implement) and appends a dry-run change proposal; it proposes, never applies or mergesan llm:// connector and AGENT_WORKSPACE_ROOT for the read-mostly code tools to see a repo

Both of these are self-sourcing and deliberately cross-tenant — one schedule serves every org, so they’re meant to run behind exactly one system-wide JobSchedule, not per user. Without that schedule they never fire and the work silently doesn’t happen.

PresetPipelineRequires
generateInvoicesgenerate_invoices — bills every InvoiceSchedule whose nextInvoiceDate is due: claims each atomically (two overlapping fires can’t double-invoice), creates and finalizes the invoice on the org’s connected account, emailing the customerSTRIPE_SECRET_KEY, plus a connected Stripe account with chargesEnabled per selling org
trialLifecycleEmailstrial_lifecycle_emails — the day-7 nudge, day-2 warning and expired win-back, with a lifecycle_emails ledger row per (org, kind) so a daily tick can’t re-sendRESEND_API_KEY and SMTP_FROM (unset ⇒ Resend’s sandbox sender, which silently drops mail to anyone but the account’s own verified address)

No Gmail → Notion preset. The dashboard’s “Build custom flow” composer (/scheduled-jobs/new) lets a user assemble read_gmail → read_url → enrich → dispatch(notion) → tag — or any other chain — themselves, picking a connected Notion connector as the destination instead of a preset-baked one. The API has always accepted an arbitrary template.steps array, so a hardcoded example preset was redundant.

No publish preset. socialPublish / blogPublish / emailPublish were each a lone publish step, and the first two had become the same chain once the post destination dissolved the social/blog split. Publishing is triggered by the row’s own lifecycle: set a post to reviewed (or a campaign to approved) and the publish worker claims it. The dashboard’s “Publish now” / “Send now” enqueues a one-off publish job to run it immediately; for a recurring pass, build a custom flow carrying the publish step.

Deleting a preset is safe by construction — JobPreset.stepsFor throws UnknownJobPresetError rather than falling back to a default chain, so a stored schedule pointing at a retired name becomes loudly broken instead of quietly running an unrelated read + analyze pass. If you hit that error, delete the schedule and recreate it against a registered preset (Retiring a preset).

RetiredWhy
quote_requests, quotesToBoard, quoteToSalesQuotes are now sales Deals — Tasks with destination.scheme = 'sales' — living on the Task kanban. The quote_requests table was backfilled and dropped along with the read_quote_requests / mark_quote_requests_reviewed / assign_to_member steps.
gmailRead the whole inbox → enrich → export with no idempotency (no label filter, no processed tag), so repeated runs re-ingested the same mail. Superseded by the label-scoped, tag-idempotent Gmail presets above.
twitterFocus, bookmarkTheir read step pointed at source readers (twitter / ChromeBookmarks) that were never implemented or wired into the ingestion sources map — bookmark threw, twitterFocus silently no-op’d. Re-add either only alongside a real ISourceReader.
classifiedsWatchThe classifieds vertical was folded into packages/platform-ingestion/; the preset, the read_classifieds step and the CDP strategies’ own API vertical were deleted with it. The generic watch step survives (it’s subject-agnostic) and is still reachable from a custom flow.
veille, blogThe one hardcoded read_gmail → read_url → enrich → dispatch(…) → tag examples, redundant once the “Build custom flow” composer exposed that path directly. veilleToBoard (same front half, dispatching to the board) is untouched.
socialPublish, blogPublish, emailPublishSee “No publish preset” above.

Presets are declared in JobPreset in the domain package — the single source of truth for the preset → steps mapping plus the per-preset input schema. See Author a preset.