Skip to main content

Triggers

Unified scheduling + event dispatch. Cron schedules, named events, and HTTP webhooks — all through one API.

Inline Task Triggers

Define cron or event triggers directly in task() — serve() auto-registers on deploy

Event Fan-out

publishEvent() dispatches to all matching event triggers simultaneously

HTTP Webhooks

POST to any URL on a schedule — works with Python, Go, Ruby, any language

Full CRUD

Create, pause, resume, fire, and delete triggers via SDK or console

Source types and Target types

A trigger has a source (when to fire) and a target (what to dispatch to).
Sources: cron (time-based), event (named event via publishEvent)
Targets: task (TypeScript handler), http (any URL), run (container — coming soon)

Inline Cron Trigger (Recommended)

The simplest way: define the trigger inside your task. serve() automatically registers it when your app first starts — fully idempotent.

tasks/daily-cleanup.ts
import { task, serve } from '@sylphx/sdk/tasks'

export const dailyCleanup = task({
  id: 'daily-cleanup',
  trigger: { cron: '0 2 * * *' },   // fires every day at 2am UTC
  run: async (_, { step }) => {
    await step.run('delete-sessions', () =>
      db.sessions.deleteMany({ where: { updatedAt: { lt: daysAgo(30) } } })
    )
    await step.run('notify', () =>
      slack.send('#ops', 'Daily cleanup complete')
    )
  },
})

// app/api/tasks/route.ts — serve() calls TriggersClient.create() on first request
export const { GET, POST } = serve({ tasks: [dailyCleanup] })

Event Triggers

Fire tasks when something happens in your app — user signs up, order is placed, payment fails. Fan-out to multiple handlers from a single publishEvent() call.

tasks/user-signup.ts
import { task, serve } from '@sylphx/sdk/tasks'
import { TriggersClient, createConfig } from '@sylphx/sdk'

// Handler A — sends welcome email
export const sendWelcomeEmail = task({
  id: 'send-welcome-email',
  trigger: { event: 'user.signup' },
  run: async ({ userId }, { step }) => {
    const user = await step.run('fetch', () => db.users.findUnique({ where: { id: userId } }))
    await step.run('email', () => mailer.welcome(user.email, user.name))
  },
})

// Handler B — sync to CRM
export const syncToCrm = task({
  id: 'sync-to-crm',
  trigger: { event: 'user.signup' },
  run: async ({ userId, plan }) => {
    await crm.contacts.upsert({ externalId: userId, plan })
  },
})

// From your auth handler — both tasks fire automatically
const config = createConfig({ secretKey: process.env.SYLPHX_SECRET_KEY! })
await TriggersClient.publishEvent(config, 'user.signup', {
  userId: newUser.id,
  plan: newUser.plan,
  email: newUser.email,
})

HTTP Triggers (any language)

Schedule a POST to any URL — no TypeScript required. Your Python/Go/Ruby service just needs an HTTP endpoint.

register-http-trigger.ts
import { TriggersClient, createConfig } from '@sylphx/sdk'

const config = createConfig({
  secretKey: process.env.SYLPHX_SECRET_KEY!,
  ref: process.env.SYLPHX_PROJECT_REF!,
})

// Cron → HTTP
await TriggersClient.create(config, {
  name: 'nightly-backup',
  source: { type: 'cron', expression: '0 3 * * *' },
  target: {
    type: 'http',
    url: 'https://my-python-service.internal/api/backup',
    headers: { Authorization: `Bearer ${process.env.INTERNAL_SECRET}` },
    payload: { type: 'full', compress: true },
  },
  idempotencyKey: 'nightly-backup-v1',  // prevents duplicates across deploys
})

// Event → HTTP
await TriggersClient.create(config, {
  name: 'notify-slack-on-payment',
  source: { type: 'event', eventName: 'payment.succeeded' },
  target: {
    type: 'http',
    url: 'https://hooks.slack.com/services/xxx/yyy/zzz',
    payload: { text: 'Payment received! 💰' },
  },
})

Managing Triggers

manage-triggers.ts
import { TriggersClient, createConfig } from '@sylphx/sdk'

const config = createConfig({ secretKey: process.env.SYLPHX_SECRET_KEY! })

// List all triggers
const { triggers } = await TriggersClient.list(config)

// Get a specific trigger
const trigger = await TriggersClient.get(config, 'sched_abc123')

// Pause / Resume
await TriggersClient.pause(config, trigger.id)
await TriggersClient.resume(config, trigger.id)

// Fire immediately (one-shot, regardless of schedule)
await TriggersClient.fire(config, trigger.id)

// Update schedule
await TriggersClient.update(config, trigger.id, {
  source: { type: 'cron', expression: '0 4 * * *' },  // move from 3am to 4am
})

// Delete
await TriggersClient.delete(config, trigger.id)

API Reference

PropertyTypeDescription
source.typerequired"cron" | "event"When to fire: cron = time-based, event = named event trigger.
source.expressionstringStandard 5-field cron expression (when source.type = "cron").
source.eventNamestringEvent name to listen for (when source.type = "event"). e.g. "user.signup".
target.typerequired"task" | "http"What to dispatch to.
target.taskNamestringTask ID to dispatch (when target.type = "task").
target.urlstringURL to POST to (when target.type = "http").
idempotencyKeystringOptional dedup key — prevents duplicate trigger creation per project.
pausedbooleanCreate in paused state (default: false — active immediately).

TriggersClient.publishEvent()

PropertyTypeDescription
eventNamerequiredstringName of the event. Matches against all active event triggers.
payloadRecord<string, unknown>Data forwarded to all matching handlers. Merged with trigger base payload.

Event payload merging

The final payload received by your task handler is: {...triggerPayload, ...eventPayload, _event: { name, publishedAt }}. Use _event.name to know which event fired your task.