> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://fyno.io/docs/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://fyno.io/docs/_mcp/server.

# Notify multiple users

POST https://api.fyno.io/v1/{WSID}/{version}/event/bulk
Content-Type: application/json

This API enables you to notify multiple users with one event trigger.

Before firing your first notification event, you must perform the following actions in your Fyno account:
- Create a Template
- Create an Event
- Create an API Key
- And obtain your workspace ID from the API Keys page.


Reference: https://fyno.io/docs/api-reference/notify-multiple-users

## Authentication

- `Authorization` header (bearer token, required) — Enter your API Key. If you don't have it already, you can create one from the API Keys page within your Fyno account

## Request

### Path parameters

- `WSID` (string, required) — Enter your workspace ID. You can obtain this value from the API Keys page within your Fyno account.
- `version` (string, required) — Enter the version for which you wish to fire the notification.

### Body (application/json)

This endpoint expects an event_bulk_body.

- `batch` (list of EventBulkBodyBatchItems, required) — Enter an array of recipients. Each item in the array must specify at least `distinct_id` or `to`. Choose one of the three options below for each item. `event` is set once at the top level of the payload and applies to every item in the batch. **Option 1: `to` only** Required per item: `to` The event uses all destination values from the `to` object, as they are. ```json { "event": "event_name", "batch": [ { "to": { "email": "abcde@fghi.com", "sms": "+49XXXXXXXXX" }, "data": { "placeholder_key_1": "placeholder_value_1" } } ] } ``` **Option 2: `distinct_id` only** Required per item: `distinct_id` The event uses all destination values (such as SMS, WhatsApp, Email, Push, and Voice) from the user's profile and sends the notification. ```json { "event": "event_name", "batch": [ { "distinct_id": "XXXXXXXX", "data": { "placeholder_key_1": "placeholder_value_1" } } ] } ``` **Option 3: both `distinct_id` and `to`** Required per item: `distinct_id`, `to` The `to` and `distinct_id` channel data merges, with the `to` object taking precedence, and the notification is sent. The sent notification is tagged to the ID specified, which you can then use to search the logs by `distinct_id`. ```json { "event": "event_name", "batch": [ { "distinct_id": "XXXXXXXX", "to": { "email": "abcde@fghi.com" }, "data": { "placeholder_key_1": "placeholder_value_1" } } ] } ``` You can mix options across items in the same batch. `data` and `callback` are optional in all three.
- `event` (string, required) — Enter the name of the event you wish to fire.

## Response

### 202

Request accepted successfully and it will be processed asynchronously.

- `event` (string, optional)
- `received_time` (double, optional)
- `request_id` (string, optional)
- `response` (list of inline_response_202_1_response, optional)

## Errors

### 400 Bad Request Error

Event name does not exist

- `_message` (string, required)
- `status` (string, required)

### 401 Unauthorized Error

API Key or Workspace ID is invalid. User is unauthorised.

- `_message` (string, required)
- `status` (string, required)

## Types

### EventBulkBodyBatchItems

### inline_response_202_1_response

- `discord` (inline_response_202_response_discord, optional)
- `email` (inline_response_202_response_email, optional)
- `inapp` (inline_response_202_response_inapp, optional)
- `push` (inline_response_202_response_push, optional)
- `seq` (integer, optional)
- `slack` (inline_response_202_response_slack, optional)
- `sms` (inline_response_202_response_sms, optional)
- `teams` (inline_response_202_response_teams, optional)
- `voice` (inline_response_202_response_sms, optional)
- `whatsapp` (inline_response_202_response_sms, optional)

### Option 1: to only

- `to` (to, required)
- `callback` (callback, optional) — (Optional) You can enable callback for an event by specifying the `callback` attribute in the payload of the event. By enabling callback, you will be able to receive delivery status of the events onto your webhook URL specified in the "Allowlist URL" section of the application. See [Configuring Allowlist URL/SQS to receive Delivery Report](../../configuring-allowlist-url-sqs-to-receive-delivery-report) for more details.
- `data` (data, optional) — (Optional) Enter the keys and values of your replaceabale placeholders. These values are replaced in the respective keys of the template when the notification is sent.

### Option 2: distinct_id only

- `distinct_id` (string, required) — Enter the distinct ID that you use to identify the recipient.
- `callback` (callback, optional) — (Optional) You can enable callback for an event by specifying the `callback` attribute in the payload of the event. By enabling callback, you will be able to receive delivery status of the events onto your webhook URL specified in the "Allowlist URL" section of the application. See [Configuring Allowlist URL/SQS to receive Delivery Report](../../configuring-allowlist-url-sqs-to-receive-delivery-report) for more details.
- `data` (data, optional) — (Optional) Enter the keys and values of your replaceabale placeholders. These values are replaced in the respective keys of the template when the notification is sent.

### Option 3: distinct_id and to

- `distinct_id` (string, required) — Enter the distinct ID that you use to identify the recipient.
- `to` (to, required)
- `callback` (callback, optional) — (Optional) You can enable callback for an event by specifying the `callback` attribute in the payload of the event. By enabling callback, you will be able to receive delivery status of the events onto your webhook URL specified in the "Allowlist URL" section of the application. See [Configuring Allowlist URL/SQS to receive Delivery Report](../../configuring-allowlist-url-sqs-to-receive-delivery-report) for more details.
- `data` (data, optional) — (Optional) Enter the keys and values of your replaceabale placeholders. These values are replaced in the respective keys of the template when the notification is sent.

### inline_response_202_response_discord

- `destination` (string, required)
- `msg_id` (string, required)
- `status` (string, required)

### inline_response_202_response_email

- `destination` (string, required)
- `msg_id` (string, required)
- `status` (string, required)

### inline_response_202_response_inapp

- `destination` (string, required)
- `msg_id` (string, required)
- `status` (string, required)

### inline_response_202_response_push

- `destination` (string, required)
- `msg_id` (string, required)
- `status` (string, required)

### inline_response_202_response_slack

- `destination` (string, required)
- `msg_id` (string, required)
- `status` (string, required)

### inline_response_202_response_sms

- `destination` (string, required)
- `msg_id` (string, required)
- `status` (string, required)

### inline_response_202_response_teams

- `destination` (string, required)
- `msg_id` (string, required)
- `status` (string, required)

### to

- `discord` (string, optional) — Enter the discord ID of the user.
- `email` (string, optional) — Enter the email address of the user.
- `inapp` (string, optional) — Enter the distinct ID or the generated token of the user.
- `push` (string, optional) — If you use multiple Push providers, add the following prefix for the respective provider tokens: For Expo: `expo_token:`\<your\_pushtoken> For FCM: `fcm_token:`\<your\_pushtoken> For OneSignal playerId: `onesignal_player_id:`\<enter\_playerid> For OneSignal externalId: `onesignal_external_id:`\<enter\_externalid> For Mi Push: `mi_token:`\<your\_pushtoken> If you use just one provider, then the prefix is not required. However, if you wish to use OneSignal with playerId token, then the prefix is required.
- `slack` (string, optional) — Enter the channel ID, user ID, or email address of the user.
- `sms` (string, optional) — Enter the mobile number of the user. The number must start with the country code (preferably in E.164 format). For example: +49XXXXXXXXX.
- `teams` (string, optional) — Enter the channel name. For teams, we currently support sending to one channel only. To send to multiple channels, do get in touch and we can share a workaround.
- `voice` (string, optional) — Enter the mobile number of the user. The number must start with the country code (preferably in E.164 format). For example: +49XXXXXXXXX.
- `whatsapp` (string, optional) — Enter the WhatsApp mobile number of the user. The number must start with the country code (preferably in E.164 format). For example: +49XXXXXXXXX

### callback

(Optional) You can enable callback for an event by specifying the `callback` attribute in the payload of the event. By enabling callback, you will be able to receive delivery status of the events onto your webhook URL specified in the "Allowlist URL" section of the application. See [Configuring Allowlist URL/SQS to receive Delivery Report](../../configuring-allowlist-url-sqs-to-receive-delivery-report) for more details.

- `custom_id` (string, required) — Use this parameter to send an ID that you would want to receive as a response (for reconciliation) in your delivery callback.
- `custom1` (string, optional) — (Optional) Use this parameter to send another ID that you want to receive as a response (for reconciliation) in your delivery callback.
- `custom2` (string, optional) — (Optional) Use this parameter to send yet another ID that you want to receive as a response (for reconciliation) in your delivery callback.
- `custom3` (string, optional) — (Optional) Use this parameter to send yet another ID that you want to receive as a response (for reconciliation) in your delivery callback.
- `enable` (boolean, optional) — (Optional) Set `true` to enable delivery callback or `false` to disable delivery callback. The default value is `true` if `custom_id` is specified.
- `allowlist_url` (CallbackAllowlistUrl, optional) — (Optional) Allowlist URL name where you want to receive delivery reports. Can be array of Allowlist URL names too if you want the delivery reports in multiple URLs. If `allowlist_url` is not specified, but callback object has `enable=true` and `custom_id` is specified, then the delivery reports will be sent to the default endpoint set to receive delivery reports in the [Configuring Allowlist URL](../../configuring-allowlist-url-sqs-to-receive-delivery-report) settings.

### data

(Optional) Enter the keys and values of your replaceabale placeholders. These values are replaced in the respective keys of the template when the notification is sent.

### CallbackAllowlistUrl

(Optional) Allowlist URL name where you want to receive delivery reports. Can be array of Allowlist URL names too if you want the delivery reports in multiple URLs. If `allowlist_url` is not specified, but callback object has `enable=true` and `custom_id` is specified, then the delivery reports will be sent to the default endpoint set to receive delivery reports in the [Configuring Allowlist URL](../../configuring-allowlist-url-sqs-to-receive-delivery-report) settings.

## Examples

**Request**

```json
{
  "batch": [
    {
      "to": {}
    }
  ],
  "event": "event_name"
}
```

**Response**

```json
{
  "event": "event_name",
  "received_time": 1672991866453,
  "request_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX",
  "response": [
    {
      "discord": {
        "destination": "101XXXXXXXXXX7XXXXX",
        "msg_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:XXXX",
        "status": "ok"
      },
      "email": {
        "destination": "abcde@fghi.com",
        "msg_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:XXXX",
        "status": "ok"
      },
      "inapp": {
        "destination": "12345",
        "msg_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:XXXX",
        "status": "ok"
      },
      "push": {
        "destination": "ExponentPushToken[XXXXX]",
        "msg_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:XXXX",
        "status": "ok"
      },
      "seq": 1,
      "slack": {
        "destination": "CXXXXXXXXXX",
        "msg_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:XXXX",
        "status": "ok"
      },
      "sms": {
        "destination": "+49XXXXXXXXX",
        "msg_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:XXXX",
        "status": "ok"
      },
      "teams": {
        "destination": "General",
        "msg_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:XXXX",
        "status": "ok"
      },
      "voice": {
        "destination": "+49XXXXXXXXX",
        "msg_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:XXXX",
        "status": "ok"
      },
      "whatsapp": {
        "destination": "+49XXXXXXXXX",
        "msg_id": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX:XXXX",
        "status": "ok"
      }
    }
  ]
}
```

**SDK Code**

```python
import requests

url = "https://api.fyno.io/v1/FYXXXXXXXX/version/event/bulk"

payload = {
    "batch": [{ "to": {} }],
    "event": "event_name"
}
headers = {
    "Authorization": "Bearer <apiKey>",
    "Content-Type": "application/json"
}

response = requests.post(url, json=payload, headers=headers)

print(response.json())
```

```javascript
const url = 'https://api.fyno.io/v1/FYXXXXXXXX/version/event/bulk';
const options = {
  method: 'POST',
  headers: {Authorization: 'Bearer <apiKey>', 'Content-Type': 'application/json'},
  body: '{"batch":[{"to":{}}],"event":"event_name"}'
};

try {
  const response = await fetch(url, options);
  const data = await response.json();
  console.log(data);
} catch (error) {
  console.error(error);
}
```

```go
package main

import (
	"fmt"
	"strings"
	"net/http"
	"io"
)

func main() {

	url := "https://api.fyno.io/v1/FYXXXXXXXX/version/event/bulk"

	payload := strings.NewReader("{\n  \"batch\": [\n    {\n      \"to\": {}\n    }\n  ],\n  \"event\": \"event_name\"\n}")

	req, _ := http.NewRequest("POST", url, payload)

	req.Header.Add("Authorization", "Bearer <apiKey>")
	req.Header.Add("Content-Type", "application/json")

	res, _ := http.DefaultClient.Do(req)

	defer res.Body.Close()
	body, _ := io.ReadAll(res.Body)

	fmt.Println(res)
	fmt.Println(string(body))

}
```

```ruby
require 'uri'
require 'net/http'

url = URI("https://api.fyno.io/v1/FYXXXXXXXX/version/event/bulk")

http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true

request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <apiKey>'
request["Content-Type"] = 'application/json'
request.body = "{\n  \"batch\": [\n    {\n      \"to\": {}\n    }\n  ],\n  \"event\": \"event_name\"\n}"

response = http.request(request)
puts response.read_body
```

```java
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.Unirest;

HttpResponse<String> response = Unirest.post("https://api.fyno.io/v1/FYXXXXXXXX/version/event/bulk")
  .header("Authorization", "Bearer <apiKey>")
  .header("Content-Type", "application/json")
  .body("{\n  \"batch\": [\n    {\n      \"to\": {}\n    }\n  ],\n  \"event\": \"event_name\"\n}")
  .asString();
```

```php
<?php
require_once('vendor/autoload.php');

$client = new \GuzzleHttp\Client();

$response = $client->request('POST', 'https://api.fyno.io/v1/FYXXXXXXXX/version/event/bulk', [
  'body' => '{
  "batch": [
    {
      "to": {}
    }
  ],
  "event": "event_name"
}',
  'headers' => [
    'Authorization' => 'Bearer <apiKey>',
    'Content-Type' => 'application/json',
  ],
]);

echo $response->getBody();
```

```csharp
using RestSharp;

var client = new RestClient("https://api.fyno.io/v1/FYXXXXXXXX/version/event/bulk");
var request = new RestRequest(Method.POST);
request.AddHeader("Authorization", "Bearer <apiKey>");
request.AddHeader("Content-Type", "application/json");
request.AddParameter("application/json", "{\n  \"batch\": [\n    {\n      \"to\": {}\n    }\n  ],\n  \"event\": \"event_name\"\n}", ParameterType.RequestBody);
IRestResponse response = client.Execute(request);
```

```swift
import Foundation

let headers = [
  "Authorization": "Bearer <apiKey>",
  "Content-Type": "application/json"
]
let parameters = [
  "batch": [["to": []]],
  "event": "event_name"
] as [String : Any]

let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "https://api.fyno.io/v1/FYXXXXXXXX/version/event/bulk")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 10.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
    print(error as Any)
  } else {
    let httpResponse = response as? HTTPURLResponse
    print(httpResponse)
  }
})

dataTask.resume()
```