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.
Gmail ingestion
Section titled “Gmail ingestion”| Preset | Pipeline | Requires |
|---|---|---|
veilleToBoard | read_gmail → read_url → enrich → dispatch(dashboard://) → tag — enriched links become kanban todo cards | connected gmail:// + llm:// connectors |
socialVeille | read_gmail → read_url → enrich → dispatch(post://…@x.com) → tag — one X post draft per link, no auto-posting | connected gmail:// + llm://, plus a connected X account (post://…@x.com) |
gmailTriage | read_gmail → triage → tag — an LLM decides whether each thread needs follow-up and drafts a kanban Task | connected gmail:// + llm://; optionally a registered Skill to steer the verdict |
getSuppliersInvoices | read_gmail → collect_invoices → tag — 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 lists | connected 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.
Board & leads
Section titled “Board & leads”| Preset | Pipeline | Requires |
|---|---|---|
calendarToTasks | calendar_to_tasks — upcoming Google Calendar events (now → +daysAhead, every accessible calendar) become one owner-scoped kanban Task each, idempotent on the event id | a gmail:// connector whose token carries the calendar.readonly scope |
captureComments | capture_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 id | at least one connected LinkedIn / Facebook Page account — with none connectable the step isn’t registered and the preset fails fast |
Agents
Section titled “Agents”| Preset | Pipeline | Requires |
|---|---|---|
agentResearch | agent — hands a goal plus a read-mostly toolset (scrape + create_task) to a reasoning loop that runs until done or maxSteps | an llm:// connector on an anthropic or openrouter provider — the tool loop is unavailable on a Cline-only provider |
implementTask | implement_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 merges | an llm:// connector and AGENT_WORKSPACE_ROOT for the read-mostly code tools to see a repo |
Operations (cross-tenant)
Section titled “Operations (cross-tenant)”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.
| Preset | Pipeline | Requires |
|---|---|---|
generateInvoices | generate_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 customer | STRIPE_SECRET_KEY, plus a connected Stripe account with chargesEnabled per selling org |
trialLifecycleEmails | trial_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-send | RESEND_API_KEY and SMTP_FROM (unset ⇒ Resend’s sandbox sender, which silently drops mail to anyone but the account’s own verified address) |
What isn’t a preset
Section titled “What isn’t a preset”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.
Retired presets
Section titled “Retired presets”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).
| Retired | Why |
|---|---|
quote_requests, quotesToBoard, quoteToSales | Quotes 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. |
gmail | Read 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, bookmark | Their 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. |
classifiedsWatch | The 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, blog | The 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, emailPublish | See “No publish preset” above. |
Defining your own
Section titled “Defining your own”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.
