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 orpkg_id.topic(required): a topic in that package'sminion.subscriptions, in its runnable version. If the package doesn't subscribe to it, the call fails and says so. Check withminion.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
paramsyourself. - Events the handler dispatches with
events.dispatchare 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' }