---
title: "Send an automation event"
method: POST
path: "/api/v1/events/send"
tags: ["events"]
---

# Send an automation event

`POST /api/v1/events/send`

## Request

- Base URL: `https://app.blueticked.com`
- URL: `https://app.blueticked.com/api/v1/events/send`
- Auth: HTTP bearer

Trigger a saved Blueticked automation from a booking system, CRM, website form or other external app.

Create an API key with the least-privilege `events:send` scope.

The contact must already exist; identify it with exactly one of contact_id, email, or phone.

Phone accepts international or South African national forms, spaces and punctuation, normalized by the callback's rule. This endpoint never creates a contact or grants consent.

HTTP 202 means accepted, not provider delivery. Read outcome.kind and outcome.outcomes (flowId, channel, kind, reason, messageIds): queued means message rows were queued, even when a later step was then refused (the refusal is kept in the automation's run history); not_sent is a refusal or failure, and a line that failed after making message rows still lists their ids in messageIds, some of which may have gone; no_flow means no listener; ran means non-send actions ran. Dry-run evaluation returns 200 with outcome: null and queues nothing. Free-text WhatsApp needs an open 24-hour reply window; an approved template is required outside it.

channel (email, sms or whatsapp) selects which automations fire. Omit it and your workspace's own live automations decide: the one channel whose automation matches this event and can reach this contact fires, and the response tells you which. When automations on more than one channel qualify, the request fails with validation_failed, and that refusal is built for programmatic recovery: error.details[0].candidates lists the qualifying channels and error.next_steps[0].action is `choose_channel`, so branch on the action, pick a candidate, and retry with channel set. Never parse the message sentence. There is no silent default, ever; the contact identifier does not choose the channel.

`events:send` is a *trigger* scope, not a send scope: it fires the automations your workspace has already configured, and those automations are the send authority, so this key never needs `messages:send` or a per-channel send scope, even though an automation may end up sending. A fired event can only run pre-authored automations for an existing contact; it cannot choose the message content, template or recipient. (This mirrors triggering a Twilio Studio flow or a Stripe workflow: the trigger credential doesn't carry the flow's send rights.)

While integrating, add test_mode: true (or use a blu_test_ key) to evaluate the automation without sending live messages. The dry-run responds 200 (not 202) with test_mode: true and a matched_filter_label alongside the matched filter and action ids. A dry run needs no connected channel: evaluation sends nothing, so it works before WhatsApp or email is set up.

Use an Idempotency-Key header so external retries do not fire duplicate actions.

## Headers

- `Idempotency-Key` string

## Request body

- union
  - object
    - `business_id` string, uuid, nullable
    - `channel` 'whatsapp' | 'sms' | 'email'
    - `event` string, required
    - `contact_id` string, uuid, required
    - `email` string
    - `phone` string: Phone in international or South African national form, normalized using the same rule as app callbacks.
    - `data` object
    - `test_mode` boolean
  - object
    - `business_id` string, uuid, nullable
    - `channel` 'whatsapp' | 'sms' | 'email'
    - `event` string, required
    - `contact_id` string, uuid
    - `email` string, required
    - `phone` string: Phone in international or South African national form, normalized using the same rule as app callbacks.
    - `data` object
    - `test_mode` boolean
  - object
    - `business_id` string, uuid, nullable
    - `channel` 'whatsapp' | 'sms' | 'email'
    - `event` string, required
    - `contact_id` string, uuid
    - `email` string
    - `phone` string, required: Phone in international or South African national form, normalized using the same rule as app callbacks.
    - `data` object
    - `test_mode` boolean

## Response `200`

Automation evaluation only: queues nothing, no provider sends or billing. outcome is null.

- object
  - `accepted` boolean, required
  - `test_mode` true, required
  - `outcome` unknown, required
  - `handled` boolean, required
  - `event` string, required
  - `channel` 'whatsapp' | 'sms' | 'email' | 'null', nullable, required
  - `business_id` string, uuid, nullable, required
  - `contact_id` string, uuid, required
  - `matched_filter_id` string
  - `matched_filter_label` string
  - `action_ids` string[]
  - `request_id` string, required

## Other responses

- `202`: Success
- `400`: Validation failed
- `401`: Unauthorized: missing or invalid API key
- `402`: Insufficient wallet balance
- `403`: Forbidden: scope, plan limit, or suspension
- `404`: Not found
- `409`: Conflict (idempotency-key reuse or resource in use)
- `413`: Request body above the endpoint's size limit
- `422`: Understood but refused: the request is well-formed, the action is not permitted for this resource
- `429`: Rate limited
- `500`: Internal error
- `502`: An upstream provider refused or failed the request
- `503`: Channel or send queue unavailable

---

[API](https://skmtc.dev/blueticked/apis/blueticked-api.md) · [All operations](https://skmtc.dev/blueticked/apis/blueticked-api/llms.txt) · [OpenAPI document](https://skmtc.dev/blueticked/apis/blueticked-api/revisions/07132284f4ff?raw)
