Webhooks
Subscribe to real-time events from Ndoto
Webhooks
Webhooks let you receive real-time event notifications from Ndoto. When an event occurs — such as a new conversation or an incoming message — Ndoto sends an HTTP POST request to your configured URL with the event data.
Base path: /api/v1/accounts/:account_id/webhooks
Available events
| Event | When it fires |
|---|---|
conversation_created | A new conversation is created |
conversation_updated | A conversation's attributes change |
conversation_status_changed | A conversation's status changes (open, resolved, etc.) |
message_created | A new message arrives in any conversation |
message_updated | A message is updated |
contact_created | A new contact is created |
contact_updated | A contact's details change |
Webhook payload
When an event fires, Ndoto sends a POST request to your URL with a JSON body:
{
"event": "message_created",
"id": "101",
"content": "Hi, I need help.",
"created_at": "2024-04-01T10:00:00.000Z",
"message_type": "incoming",
"conversation": {
"id": 42,
"status": "open",
"inbox_id": 2
},
"contact": {
"id": 10,
"name": "Jane Doe",
"email": "[email protected]"
},
"account": {
"id": 1,
"name": "Acme Support"
}
}List webhooks
Returns all webhooks configured in your account.
GET /api/v1/accounts/:account_id/webhooksResponse
{
"payload": [
{
"id": 7,
"url": "https://yourapp.com/webhooks/ndoto",
"name": "My integration",
"subscriptions": ["message_created", "conversation_created"],
"active": true
}
]
}const response = await fetch(
'https://app.usendoto.com/api/v1/accounts/1/webhooks',
{
headers: { 'api_access_token': 'YOUR_TOKEN' },
}
);
const data = await response.json();
console.log(data.payload);import requests
response = requests.get(
'https://app.usendoto.com/api/v1/accounts/1/webhooks',
headers={'api_access_token': 'YOUR_TOKEN'},
)
print(response.json())<?php
$ch = curl_init('https://app.usendoto.com/api/v1/accounts/1/webhooks');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['api_access_token: YOUR_TOKEN'],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($response);import 'package:http/http.dart' as http;
import 'dart:convert';
final response = await http.get(
Uri.parse('https://app.usendoto.com/api/v1/accounts/1/webhooks'),
headers: {'api_access_token': 'YOUR_TOKEN'},
);
print(jsonDecode(response.body));require 'net/http'
require 'json'
uri = URI('https://app.usendoto.com/api/v1/accounts/1/webhooks')
req = Net::HTTP::Get.new(uri)
req['api_access_token'] = 'YOUR_TOKEN'
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts JSON.parse(res.body)Create a webhook
Registers a new webhook endpoint.
POST /api/v1/accounts/:account_id/webhooksBody parameters
| Parameter | Type | Required | Description |
|---|---|---|---|
url | string | Yes | The URL Ndoto will POST events to |
subscriptions | array | Yes | List of event names to subscribe to |
name | string | No | A label for this webhook |
inbox_id | number | No | Scope events to a specific inbox |
const response = await fetch(
'https://app.usendoto.com/api/v1/accounts/1/webhooks',
{
method: 'POST',
headers: {
'api_access_token': 'YOUR_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://yourapp.com/webhooks/ndoto',
name: 'My integration',
subscriptions: ['message_created', 'conversation_created', 'conversation_status_changed'],
}),
}
);
const webhook = await response.json();
console.log(webhook);response = requests.post(
'https://app.usendoto.com/api/v1/accounts/1/webhooks',
headers={'api_access_token': 'YOUR_TOKEN'},
json={
'url': 'https://yourapp.com/webhooks/ndoto',
'name': 'My integration',
'subscriptions': ['message_created', 'conversation_created', 'conversation_status_changed'],
},
)
print(response.json())<?php
$ch = curl_init('https://app.usendoto.com/api/v1/accounts/1/webhooks');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://yourapp.com/webhooks/ndoto',
'name' => 'My integration',
'subscriptions' => ['message_created', 'conversation_created', 'conversation_status_changed'],
]),
CURLOPT_HTTPHEADER => [
'api_access_token: YOUR_TOKEN',
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($response);final response = await http.post(
Uri.parse('https://app.usendoto.com/api/v1/accounts/1/webhooks'),
headers: {
'api_access_token': 'YOUR_TOKEN',
'Content-Type': 'application/json',
},
body: jsonEncode({
'url': 'https://yourapp.com/webhooks/ndoto',
'name': 'My integration',
'subscriptions': ['message_created', 'conversation_created', 'conversation_status_changed'],
}),
);
print(jsonDecode(response.body));uri = URI('https://app.usendoto.com/api/v1/accounts/1/webhooks')
req = Net::HTTP::Post.new(uri, 'Content-Type' => 'application/json')
req['api_access_token'] = 'YOUR_TOKEN'
req.body = JSON.dump(
url: 'https://yourapp.com/webhooks/ndoto',
name: 'My integration',
subscriptions: ['message_created', 'conversation_created', 'conversation_status_changed']
)
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts JSON.parse(res.body)Update a webhook
Updates an existing webhook's URL or subscriptions.
PATCH /api/v1/accounts/:account_id/webhooks/:idconst response = await fetch(
'https://app.usendoto.com/api/v1/accounts/1/webhooks/7',
{
method: 'PATCH',
headers: {
'api_access_token': 'YOUR_TOKEN',
'Content-Type': 'application/json',
},
body: JSON.stringify({
subscriptions: ['message_created', 'contact_created'],
}),
}
);
const updated = await response.json();
console.log(updated);response = requests.patch(
'https://app.usendoto.com/api/v1/accounts/1/webhooks/7',
headers={'api_access_token': 'YOUR_TOKEN'},
json={'subscriptions': ['message_created', 'contact_created']},
)
print(response.json())<?php
$ch = curl_init('https://app.usendoto.com/api/v1/accounts/1/webhooks/7');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'PATCH',
CURLOPT_POSTFIELDS => json_encode([
'subscriptions' => ['message_created', 'contact_created'],
]),
CURLOPT_HTTPHEADER => [
'api_access_token: YOUR_TOKEN',
'Content-Type: application/json',
],
]);
$response = json_decode(curl_exec($ch), true);
curl_close($ch);
print_r($response);final response = await http.patch(
Uri.parse('https://app.usendoto.com/api/v1/accounts/1/webhooks/7'),
headers: {
'api_access_token': 'YOUR_TOKEN',
'Content-Type': 'application/json',
},
body: jsonEncode({'subscriptions': ['message_created', 'contact_created']}),
);
print(jsonDecode(response.body));uri = URI('https://app.usendoto.com/api/v1/accounts/1/webhooks/7')
req = Net::HTTP::Patch.new(uri, 'Content-Type' => 'application/json')
req['api_access_token'] = 'YOUR_TOKEN'
req.body = JSON.dump(subscriptions: ['message_created', 'contact_created'])
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts JSON.parse(res.body)Delete a webhook
Removes a webhook. Ndoto will stop sending events to that URL.
DELETE /api/v1/accounts/:account_id/webhooks/:idconst response = await fetch(
'https://app.usendoto.com/api/v1/accounts/1/webhooks/7',
{
method: 'DELETE',
headers: { 'api_access_token': 'YOUR_TOKEN' },
}
);
console.log(response.status); // 200response = requests.delete(
'https://app.usendoto.com/api/v1/accounts/1/webhooks/7',
headers={'api_access_token': 'YOUR_TOKEN'},
)
print(response.status_code)<?php
$ch = curl_init('https://app.usendoto.com/api/v1/accounts/1/webhooks/7');
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => ['api_access_token: YOUR_TOKEN'],
]);
curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);final response = await http.delete(
Uri.parse('https://app.usendoto.com/api/v1/accounts/1/webhooks/7'),
headers: {'api_access_token': 'YOUR_TOKEN'},
);
print(response.statusCode);uri = URI('https://app.usendoto.com/api/v1/accounts/1/webhooks/7')
req = Net::HTTP::Delete.new(uri)
req['api_access_token'] = 'YOUR_TOKEN'
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) { |http| http.request(req) }
puts res.codeReceiving webhook events
Your webhook endpoint must:
- Accept
POSTrequests - Return a
2xxHTTP status code within 10 seconds - Parse the JSON body to read the event data
Here's a minimal handler for each language:
// Express.js example
import express from 'express';
const app = express();
app.use(express.json());
app.post('/webhooks/ndoto', (req, res) => {
const { event, conversation, contact, content } = req.body;
console.log(`Event: ${event}`);
if (event === 'message_created') {
console.log(`New message from ${contact?.name}: ${content}`);
}
res.sendStatus(200);
});
app.listen(3000);# Flask example
from flask import Flask, request
app = Flask(__name__)
@app.post('/webhooks/ndoto')
def ndoto_webhook():
data = request.get_json()
event = data.get('event')
print(f"Event: {event}")
if event == 'message_created':
contact = data.get('contact', {})
print(f"New message from {contact.get('name')}: {data.get('content')}")
return '', 200<?php
$payload = json_decode(file_get_contents('php://input'), true);
$event = $payload['event'] ?? null;
error_log("Event: $event");
if ($event === 'message_created') {
$name = $payload['contact']['name'] ?? 'Unknown';
$content = $payload['content'] ?? '';
error_log("New message from $name: $content");
}
http_response_code(200);import 'dart:convert';
import 'dart:io';
Future<void> main() async {
final server = await HttpServer.bind(InternetAddress.anyIPv4, 8080);
await for (final request in server) {
if (request.method == 'POST' && request.uri.path == '/webhooks/ndoto') {
final body = await utf8.decoder.bind(request).join();
final data = jsonDecode(body) as Map<String, dynamic>;
final event = data['event'];
print('Event: $event');
if (event == 'message_created') {
final contact = data['contact'] as Map?;
print('Message from ${contact?['name']}: ${data['content']}');
}
request.response.statusCode = 200;
await request.response.close();
}
}
}# Sinatra example
require 'sinatra'
require 'json'
post '/webhooks/ndoto' do
payload = JSON.parse(request.body.read)
event = payload['event']
puts "Event: #{event}"
if event == 'message_created'
name = payload.dig('contact', 'name')
content = payload['content']
puts "New message from #{name}: #{content}"
end
status 200
end