> For the complete documentation index, see [llms.txt](https://docs.askyazi.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.askyazi.com/integrations-and-api/event-triggers-api.md).

# Event triggers API

Send a WhatsApp template message, such as a study invite, from your own system the moment something happens, using Yazi's event triggers API.

Event triggers let your own system (a CRM, booking system or app) send a WhatsApp template message to one person on demand through Yazi. The usual use is a study invite that goes out straight after a purchase, service visit or sign-up. You set up the message once as an event-triggered schedule, then call one API endpoint each time you want it sent.

{% hint style="info" %}
**In short:**

* Create an API key in **Settings > API Keys** (it is shown once), and an **Event Triggered** schedule in **Scheduled Messages**. The schedule's summary screen shows its **Configuration ID**.
* Send a message with `POST /v1/jobs`, the header `x-api-key`, the Configuration ID, a phone number in E.164 format (`+27821234567`) and your template variables.
* A successful call returns `202 Accepted` with a `jobId`. Give partners a **Trigger-only** key so they cannot read your data.
  {% endhint %}

## How it works

```
┌───────────────┐    POST /v1/jobs    ┌────────────────┐    WhatsApp API     ┌─────────────┐
│  Your system  │ ──────────────────> │      Yazi      │ ──────────────────> │  Recipient  │
│  (CRM, app)   │  phone + variables  │                │  Template message   │             │
└───────────────┘                     └────────────────┘                     └─────────────┘
```

1. You choose an approved WhatsApp template and create an event-triggered schedule for it (once).
2. When an event happens, your system calls `POST /v1/jobs` with the person's phone number and the template's variables.
3. Yazi accepts the request, then sends the template message on WhatsApp.
4. If the template has a **Start** button linked to a study, tapping it starts that study for the person.

Because event triggers send a template, they work outside WhatsApp's 24-hour window, and Meta charges for each message by template category. See [WhatsApp costs and the 24-hour window](/reach-participants/whatsapp-costs-and-the-24-hour-window.md).

## Before you start

* **An approved template.** The template must exist in your organisation and be approved by Meta. See [Broadcast templates](/reach-participants/broadcast-templates.md). For a study invite, add a **Start** quick-reply button linked to the study and an **Opt-out** button.
* **A live study**, if the message invites people to one.
* **An API key** (step 1 below).
* **A developer** who can make HTTPS requests from your system.

## Set it up

{% stepper %}
{% step %}

### Create an API key

Go to **Settings > API Keys** ("Manage API keys for the Connect Gateway API") and, in the **Create New Key** card, click **+ Create API Key**. Choose what the key may do under endpoint access:

<figure><img src="https://358667285-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FzMRQUXiGQlBwWdg6WEmG%2Fuploads%2FbFavehpY9PIAqlqc4ppr%2Ffs-settings-api-keys.webp?alt=media&amp;token=b68b8c00-139d-42c2-81ec-c2a41842557f" alt="Settings > API Keys: Create API Key, and each key with its plan, its access (read-only, full or chosen endpoints) and Deactivate or Delete"><figcaption></figcaption></figure>

| Preset           | Allows                                    | Use for                                                                                                   |
| ---------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| **Trigger-only** | `POST /v1/jobs` only                      | Partners and systems that only need to send messages. They cannot read studies, results or phone numbers. |
| **Read-only**    | Reading studies, results and participants | Reporting tools.                                                                                          |
| **Full access**  | Everything the API offers                 | Your own trusted back end.                                                                                |
| **Custom**       | The endpoints you pick                    | Anything in between.                                                                                      |

Confirm to create it. **The key value is only shown once**, so copy it into your password manager or secrets store straight away. Keys created before July 2026 have full access until you replace them.
{% endstep %}

{% step %}

### Create an event-triggered schedule

Go to **Scheduled Messages**, start a new schedule and choose **Event Triggered**. Pick the template, map its variables, and set what any buttons do (for example, **Start** opens a study). See [Scheduled messages](/reach-participants/scheduled-messages.md).

You can also create the schedule through the API (see `POST /v1/jobs/schedule` below), or ask Yazi to set it up for you.
{% endstep %}

{% step %}

### Copy the Configuration ID

The schedule's summary screen shows the **Configuration ID** and **Organisation ID**, each with tap to copy. Your system needs the Configuration ID. The Organisation ID is not needed to call `POST /v1/jobs`.
{% endstep %}

{% step %}

### Call the trigger endpoint

From your system, call `POST /v1/jobs` with the Configuration ID, the phone number and the variables. See the reference below.
{% endstep %}

{% step %}

### Check it worked

The API answers `202` with a `jobId`. In **Scheduled Messages**, the schedule's **Progress** column shows how many messages it has sent, and its send history shows delivery, reads and failures with reasons. See [Send History and statuses](/reach-participants/send-history-and-statuses.md).
{% endstep %}
{% endstepper %}

## API reference

### Base URL and authentication

**Base URL:** `https://api.askyazi.com/v1`

Every request needs your API key in this header:

```
x-api-key: your-api-key
```

{% hint style="warning" %}
The header name must be exactly `x-api-key`, all lower case.
{% endhint %}

```bash
curl -H "x-api-key: your-api-key" https://api.askyazi.com/v1/jobs/schedule
```

### Conventions

* Most fields use camelCase (`phoneNumber`, `configurationId`). The schedule endpoints (`/v1/jobs/schedule`) use snake\_case (`template_name`, `message_variables`).
* Phone numbers must be in E.164 format **with a leading `+`** and country code, for example `+27821234567`. Do not use a local format starting with 0.
* Some field names still use the older word "campaign" (for example `campaignId`). A campaign ID is a study's ID.
* Ask Yazi for the API specification if your developers want to generate a client.

### Quick reference

| Action                                     | Method | Endpoint                              |
| ------------------------------------------ | ------ | ------------------------------------- |
| List event-triggered schedules             | `GET`  | `/v1/jobs/schedule`                   |
| Create a schedule                          | `POST` | `/v1/jobs/schedule`                   |
| Update a schedule                          | `PUT`  | `/v1/jobs/schedule/{configurationId}` |
| Trigger a schedule (send a message)        | `POST` | `/v1/jobs`                            |
| List studies                               | `GET`  | `/v1/study`                           |
| Study details                              | `GET`  | `/v1/study/{studyId}`                 |
| Study results                              | `GET`  | `/v1/study/{studyId}/results`         |
| Study participants                         | `GET`  | `/v1/study/{studyId}/participants`    |
| Send a broadcast to a study's participants | `POST` | `/v1/study/{studyId}/broadcast`       |

### Trigger a schedule (send a message)

```
POST /v1/jobs
Content-Type: application/json
x-api-key: your-api-key
```

```json
{
  "configurationId": "your-config-id",
  "phoneNumber": "+27821234567",
  "variables": {
    "Name": "Thabo",
    "Topic": "your recent store visit"
  }
}
```

| Field             | Type   | Required                      | Description                                                                                        |
| ----------------- | ------ | ----------------------------- | -------------------------------------------------------------------------------------------------- |
| `configurationId` | string | Yes                           | The schedule to trigger. Find it on the schedule's summary screen or with `GET /v1/jobs/schedule`. |
| `phoneNumber`     | string | Yes                           | E.164 with a leading `+`, for example `+27821234567`.                                              |
| `variables`       | object | If the template has variables | Name and value pairs for the template's variables, including any button variables.                 |

**Response (202 Accepted):**

```json
{
  "status": "success",
  "data": {
    "jobId": "request-id",
    "type": "EVENT_TRIGGERED",
    "status": "ACCEPTED",
    "message": "Job has been accepted for processing."
  }
}
```

`202` means Yazi has accepted the job. The WhatsApp message is sent straight after. Delivery to the phone is shown in the schedule's send history, not in this response.

### Variables

Variables fill the dynamic parts of your template. If the template reads:

> Hi {{1}}, we are doing research on {{2}}. Press Start to begin.

and its parameters are named `["Name", "Topic"]`, then:

```json
"variables": {
  "Name": "Thabo Mokoena",
  "Topic": "customer service"
}
```

sends: *"Hi Thabo Mokoena, we are doing research on customer service. Press Start to begin."*

* Use the parameter names defined for the template. Matching is not case-sensitive, so `name` and `Name` are treated the same.
* `variables` is a flat object of `"name": "value"` pairs. Order is handled for you, based on the schedule's `message_variables`.
* Put button variables (for example the dynamic part of a URL button) in the same flat object.
* If a required variable is missing, the API returns `INVALID_CONFIGURATION` and lists exactly which ones it needs.

### List schedules

```bash
curl -H "x-api-key: your-api-key" https://api.askyazi.com/v1/jobs/schedule
```

Returns every event-triggered schedule in your organisation, with its ID. Use it if you have lost a Configuration ID.

### Create a schedule

```
POST /v1/jobs/schedule
Content-Type: application/json
x-api-key: your-api-key
```

```json
{
  "name": "Welcome message",
  "description": "Send a welcome message to new customers",
  "template_name": "welcome_v1",
  "message_variables": ["first_name"]
}
```

| Field               | Type      | Required | Description                                                                  |
| ------------------- | --------- | -------- | ---------------------------------------------------------------------------- |
| `name`              | string    | Yes      | A name for your reference.                                                   |
| `description`       | string    | No       | What the schedule does.                                                      |
| `template_name`     | string    | Yes      | The exact template name. It must exist in your organisation and be approved. |
| `message_variables` | string\[] | No       | The template's variable names, in order.                                     |
| `buttons`           | object\[] | No       | Button definitions (see below).                                              |
| `language`          | string    | No       | Template language code. Default `en_US`.                                     |
| `status`            | string    | No       | `ACTIVE` or `INACTIVE`. Default `ACTIVE`.                                    |

**Response (201 Created):**

```json
{
  "success": true,
  "message": "Schedule configuration created",
  "data": {
    "id": "abc12345-def6-7890-abcd-ef1234567890",
    "name": "Welcome message",
    "description": "Send a welcome message to new customers",
    "templateName": "welcome_v1",
    "organisationId": "org-uuid-here",
    "messageVariables": ["first_name"],
    "buttons": [],
    "type": "event_triggered",
    "status": "ACTIVE",
    "createdAt": "2026-01-15T10:00:00.000Z"
  }
}
```

The returned `id` is your `configurationId`.

If the template does not exist, the error lists your available templates. If it exists but Meta has not approved it yet, the error gives its current status.

### Update a schedule

```
PUT /v1/jobs/schedule/{configurationId}
```

Send only the fields you want to change. The fields are the same as for creating a schedule, and the same template checks apply.

```json
{ "status": "INACTIVE" }
```

```json
{
  "template_name": "welcome_v2",
  "message_variables": ["first_name", "last_name"]
}
```

### Templates with buttons

**URL button with a dynamic part:**

```json
{
  "name": "Survey invite",
  "template_name": "survey_invite_v2",
  "message_variables": ["first_name"],
  "buttons": [
    {
      "type": "url",
      "text": "Start survey",
      "url": "https://example.com/survey/{{survey_id}}",
      "variables": ["survey_id"]
    }
  ]
}
```

When triggering, put every variable in one flat object:

```json
{
  "configurationId": "your-config-id",
  "phoneNumber": "+27821234567",
  "variables": {
    "first_name": "Thabo",
    "survey_id": "abc-123"
  }
}
```

**Quick-reply buttons that start a study or opt the person out:**

```json
{
  "name": "Study invite",
  "template_name": "study_invite_v1",
  "message_variables": ["first_name"],
  "buttons": [
    { "type": "quick_reply", "text": "Start", "campaignId": "your-study-id" },
    { "type": "quick_reply", "text": "Opt-out" }
  ]
}
```

### Send a broadcast to a study's participants

Sends a template to all, or a filtered set of, a study's participants.

```
POST /v1/study/{studyId}/broadcast
Content-Type: application/json
x-api-key: your-api-key
```

```json
{
  "templateName": "follow_up_reminder",
  "variables": { "first_name": "Thabo" },
  "filterByActivity": "activity-uuid",
  "filterByStatus": "active"
}
```

| Field              | Type   | Required | Description                                       |
| ------------------ | ------ | -------- | ------------------------------------------------- |
| `templateName`     | string | Yes      | The template to send.                             |
| `variables`        | object | No       | Template variables.                               |
| `filterByActivity` | UUID   | No       | Only participants in this activity.               |
| `filterByStatus`   | string | No       | `active`, `completed`, `dropped_out` or `paused`. |

If a call to a study or broadcast endpoint is refused even with a Full access key, contact Yazi support to check what is enabled for your organisation.

### Paging and retries

* List endpoints return results in pages. Keep fetching until there are no more.
* If your system times out on `POST /v1/jobs`, check the schedule's send history before you retry, so the person does not receive the message twice.
* On `429`, wait and retry with a growing delay.

### Errors

All errors return JSON explaining what went wrong.

**Missing variables (400)**

```json
{
  "status": "error",
  "error": {
    "code": "INVALID_CONFIGURATION",
    "message": "Invalid job configuration.",
    "requestId": "request-uuid",
    "details": {
      "missingVariables": ["Name", "Topic"],
      "example": { "Name": "<value>", "Topic": "<value>" }
    }
  }
}
```

**Invalid phone number (400)**

```json
{
  "status": "error",
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "phoneNumber: Must be E.164 format (e.g. +27821234567)"
  }
}
```

**Schedule not found (404)**

```json
{
  "status": "error",
  "error": {
    "code": "CONFIGURATION_NOT_FOUND",
    "message": "Schedule configuration not found"
  }
}
```

**Template not found**

```json
{
  "success": false,
  "error": "Template \"nonexistent_template\" not found for this organisation",
  "details": {
    "providedTemplate": "nonexistent_template",
    "availableTemplates": ["welcome_v1", "survey_invite_v2", "study_invite_v1"]
  }
}
```

**Template not approved**

```json
{
  "success": false,
  "error": "Template \"pending_template\" is not approved (current status: PENDING)",
  "details": {
    "templateName": "pending_template",
    "templateStatus": "PENDING",
    "hint": "Only templates with status APPROVED can be used. Check your Meta Business Manager."
  }
}
```

**Missing or invalid API key (401)**

```json
{
  "status": "error",
  "error": {
    "code": "MISSING_API_KEY",
    "message": "An x-api-key header is required. Include your API key in the request.",
    "requestId": "request-uuid"
  }
}
```

`INVALID_API_KEY` is returned the same way when the key is wrong. A key that is valid but not allowed to use an endpoint (for example a Trigger-only key calling `/v1/study`) is refused, and the attempt is recorded in your audit log.

### Status codes

| Status | Meaning                               |
| ------ | ------------------------------------- |
| 200    | Success                               |
| 201    | Created                               |
| 202    | Accepted for processing               |
| 400    | Bad request. Read the error message.  |
| 401    | Missing or invalid API key            |
| 404    | Not found (study, schedule and so on) |
| 429    | Rate limit exceeded. Wait and retry.  |
| 500    | Server error. Contact Yazi support.   |

### Postman

Yazi can give you a Postman collection. Import it, then set the collection variables `baseUrl`, `apiKey`, `studyId` (from List studies) and `configurationId`.

## Limits and things to know

* **One phone number per call.** To message a list, use a broadcast or a CSV upload schedule instead.
* **Templates only.** Event triggers send approved templates. Free-text messages are not possible, and template changes need Meta approval again.
* **Opt-outs.** Keep your own list of people who opted out, and do not call the API for them.
* **Keep keys secret.** Anyone with a Full access key can read your study data. Use Trigger-only keys for partners, store keys in a secrets manager, and contact Yazi support straight away if a key leaks.
* **API key actions are audited.** Requests made with API keys, including refused ones, are recorded in your organisation's audit log.
* **Number quality.** Messaging people who do not expect your message can lower your WhatsApp number's quality. See [Number health and limits](/set-up-whatsapp/number-health-and-limits.md).

## Related

* [Scheduled messages](/reach-participants/scheduled-messages.md)
* [Broadcast templates](/reach-participants/broadcast-templates.md)
* [Response Push webhooks](/integrations-and-api/response-push-webhooks.md)
* [Send History and statuses](/reach-participants/send-history-and-statuses.md)
* [WhatsApp costs and the 24-hour window](/reach-participants/whatsapp-costs-and-the-24-hour-window.md)
