Skip to docs content
Browse documentation
On this page

Synthetic event dispatch

Owners and admins run one package's subscription handler right away with minion.events.dispatchSynthetic to check it after publishing. A real run with real side effects; filters are skipped and the envelope carries synthetic: true.

minion.events.dispatchSynthetic runs one package's subscription handler right away, with a payload you choose. Use it after publishing to check a handler works without waiting for a real event.

It is a real run with real side effects. The handler runs as its publisher, with the same storage, sql(), secrets and capabilities it has in production. If it sends email or writes data, that happens.

Only owners and admins of the organization can call it, the same rule as running a job with custom params.

Call it

const result = await minion.events.dispatchSynthetic({
  package: 'error-pager',            // package name or pkg_ id
  topic: 'run.error.recorded',       // a topic it subscribes to
  params: {                          // becomes the envelope's payload
    runId: 'run_smoke',
    source: 'job',
    status: 'failed',
    error: { kind: 'Error', message: 'Synthetic smoke failure' },
    finishedAt: new Date().toISOString(),
    runUrl: 'https://app.minion.codes/app/runs/run_smoke',
  },
})
// { package, topic, deliveryId, status: 'started' | 'start_failed' | 'none', runIdempotencyKey }

Fields:

  • package (required): the subscribing package's name or pkg_ id.
  • topic (required): a topic in that package's minion.subscriptions, in its runnable version. If the package doesn't subscribe to it, the call fails and says so. Check with minion.events.subscriptionsList.
  • params (optional): the payload, a JSON object up to 64 KB. Include only the fields your handler reads. Payload shapes for each topic are on Package subscriptions and events .

The handler receives the normal envelope with synthetic: true, source: { type: 'platform' } and a generated idempotencyKey.

What differs from a real event

  • Only the named package runs. Other subscribers to the topic don't.
  • Filters are not applied. To test filter-dependent code, put the matching fields in params yourself.
  • Events the handler dispatches with events.dispatch are dropped, so a smoke test can't set off a chain.
  • A failing synthetic run doesn't emit run.error.recorded.
  • It still counts against the plan's daily event-run quota.

Check the result

The call returns as soon as the run starts. Follow it with:

const { deliveries } = await minion.events.deliveriesList({ packageId: 'pkg_…', limit: 1 })
// deliveries[0].status: 'running' | 'succeeded' | 'failed' | ..., deliveries[0].runId
const run = await minion.runs.get({ id: deliveries[0].runId })

Synthetic deliveries show synthetic: true. The run also appears in Activity .

Guarding side effects

Handlers should behave the same for synthetic and real events, so the test means something. If a handler does something you can't undo (charging a card, emailing a customer), check event.synthetic and skip just that step, visibly:

if (event.synthetic) return { skipped: 'would have charged the card' }