Get event responses
After you send a calendar event, guests answer on WhatsApp: going, not going or maybe, with or without extra guests. This page shows how to read those answers. Each guest has one answer per event, and the latest replaces the previous one.
To be told the moment someone answers, use the message.calendar_responded webhook event (see Calendar events). Answers given before this feature existed are not available.
Get responses
Endpoint: GET /v1/messages/{message_id}/responses for the main number. For extra numbers, use GET /v1/numbers/{ref}/messages/{message_id}/responses, where {ref} is the number’s current alias or its id (num_…, the message’s number_id).
Required scope: messages:read
Path parameters:
| Parameter | Type | Description |
|---|---|---|
ref | string | (only on the extra number route) Alias of the number the event belongs to (main or the alias of an extra number) or its id (num_…). |
message_id | string | Public id of the calendar event (msg_{hex32}), the same one the send returned. |
curl -X GET \ https://api.turbonotify.com/v1/messages/msg_01993c5a1b24d3e9f01a2b3c4d5e6f70/responses \ -H "Authorization: Bearer YOUR_KEY"const response = await fetch( 'https://api.turbonotify.com/v1/messages/msg_01993c5a1b24d3e9f01a2b3c4d5e6f70/responses', { method: 'GET', headers: { 'Authorization': 'Bearer YOUR_KEY' } });
const result = await response.json();console.log(result);import requests
url = "https://api.turbonotify.com/v1/messages/msg_01993c5a1b24d3e9f01a2b3c4d5e6f70/responses"headers = { "Authorization": "Bearer YOUR_KEY"}
response = requests.get(url, headers=headers)result = response.json()print(result)package main
import ( "fmt" "io" "net/http")
func main() { req, _ := http.NewRequest("GET", "https://api.turbonotify.com/v1/messages/msg_01993c5a1b24d3e9f01a2b3c4d5e6f70/responses", nil) req.Header.Set("Authorization", "Bearer YOUR_KEY") resp, _ := http.DefaultClient.Do(req) defer resp.Body.Close() body, _ := io.ReadAll(resp.Body) fmt.Println(string(body))}<?php$url = 'https://api.turbonotify.com/v1/messages/msg_01993c5a1b24d3e9f01a2b3c4d5e6f70/responses';
$options = [ 'http' => [ 'header' => [ 'Authorization: Bearer YOUR_KEY' ], 'method' => 'GET' ]];
$context = stream_context_create($options);$result = file_get_contents($url, false, $context);$response = json_decode($result, true);var_dump($response);?>var client = new HttpClient();client.DefaultRequestHeaders.Add("Authorization", "Bearer YOUR_KEY");
var response = await client.GetAsync( "https://api.turbonotify.com/v1/messages/msg_01993c5a1b24d3e9f01a2b3c4d5e6f70/responses");var result = await response.Content.ReadAsStringAsync();Console.WriteLine(result);import java.net.http.*;import java.net.URI;
public class TurboNotify { public static void main(String[] args) throws Exception { var client = HttpClient.newHttpClient();
var request = HttpRequest.newBuilder() .uri(URI.create("https://api.turbonotify.com/v1/messages/msg_01993c5a1b24d3e9f01a2b3c4d5e6f70/responses")) .header("Authorization", "Bearer YOUR_KEY") .GET() .build();
var response = client.send(request, HttpResponse.BodyHandlers.ofString()); System.out.println(response.body()); }}Response examples
With responses
Responses are listed from the newest to the oldest.
{
"message_id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f70",
"counts": {
"going": 2,
"not_going": 1,
"maybe": 1,
"extra_guests": 3
},
"responses": [
{
"from": {
"number": "5511999990004",
"name": "Marina Costa"
},
"response": "going",
"at": "2026-11-03T15:30:00Z"
},
{
"from": {
"number": "5511999990003",
"name": "Carlos Lima"
},
"response": "maybe",
"extra_guests": 1,
"at": "2026-11-03T12:10:00Z"
},
{
"from": {
"number": "5511999990002",
"contact_id": "ct_a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4",
"name": "Ana Souza"
},
"response": "going",
"extra_guests": 2,
"at": "2026-11-03T12:00:00Z"
},
{
"from": {
"number": "5511999990005"
},
"response": "not_going",
"at": "2026-11-03T11:45:00Z"
}
]
} No responses
{
"message_id": "msg_01993c5a1b24d3e9f01a2b3c4d5e6f70",
"counts": {
"going": 0,
"not_going": 0,
"maybe": 0,
"extra_guests": 0
},
"responses": []
} Response structure
| Field | Type | Description |
|---|---|---|
message_id | string | Public id of the event. |
counts | object | Totals of the latest answers. Always carries the four keys. |
counts.going | integer | Guests whose latest answer is going. |
counts.not_going | integer | Guests whose latest answer is not_going. |
counts.maybe | integer | Guests whose latest answer is maybe. |
counts.extra_guests | integer | Total extra guests brought by those who answered going or maybe. |
responses | array | The latest answer of each guest, from the newest to the oldest. |
responses[].from | object | The guest. |
responses[].from.number | string | Guest phone number (without +). |
responses[].from.name | string | The guest’s name on WhatsApp. Omitted when not available. |
responses[].from.contact_id | string | Public id of the contact (ct_{hex32}). Omitted when not available. |
responses[].response | string | going, not_going or maybe. |
responses[].extra_guests | integer | Extra guests the person brings. Appears only when greater than zero. |
responses[].at | string | ISO 8601 date and time when the answer was given. |
The data follows the same period as reactions: answers are kept for 12 months together with the message. The content of the event (name, description, place) follows your plan’s content period. See Retention.
Possible errors
| Status | Error code | Description |
|---|---|---|
400 | invalid_message_id | The message_id is not in the msg_{hex32} format. |
400 | message_not_calendar | The message is not a calendar event. |
401 | not_authenticated | The API key is invalid or missing. |
403 | insufficient_scope | The API key does not have the messages:read scope. |
404 | not_found | The message was not found, belongs to another organization, or belongs to a number other than the one in the URL (on the shortcut route without {ref}, the main number): all of these cases return the same response, with no distinction. |
404 | number_not_found | (only on the route with {ref}) The alias or id in the URL does not match any registered number. |
400 | invalid_number_id | (only on the route with {ref}) The value in the URL starts with num_ but is not a valid number id. |
500 | internal_error | Unexpected internal server error. |