curl --request POST \
--url https://api.isnap.ai/v1/webhooks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"url": "https://hooks.your-domain.example/isnap",
"events": [
"message.received",
"line.*"
],
"line_ids": [
"line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1"
]
}
'import requests
url = "https://api.isnap.ai/v1/webhooks"
payload = {
"url": "https://hooks.your-domain.example/isnap",
"events": ["message.received", "line.*"],
"line_ids": ["line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1"]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: 'https://hooks.your-domain.example/isnap',
events: ['message.received', 'line.*'],
line_ids: ['line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1']
})
};
fetch('https://api.isnap.ai/v1/webhooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.isnap.ai/v1/webhooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://hooks.your-domain.example/isnap',
'events' => [
'message.received',
'line.*'
],
'line_ids' => [
'line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.isnap.ai/v1/webhooks"
payload := strings.NewReader("{\n \"url\": \"https://hooks.your-domain.example/isnap\",\n \"events\": [\n \"message.received\",\n \"line.*\"\n ],\n \"line_ids\": [\n \"line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.isnap.ai/v1/webhooks")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"https://hooks.your-domain.example/isnap\",\n \"events\": [\n \"message.received\",\n \"line.*\"\n ],\n \"line_ids\": [\n \"line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.isnap.ai/v1/webhooks")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"url\": \"https://hooks.your-domain.example/isnap\",\n \"events\": [\n \"message.received\",\n \"line.*\"\n ],\n \"line_ids\": [\n \"line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"id": "wh_3Kp8Rn9Fh2Lm5Qx7Vw1Yz4Bc6",
"url": "<string>",
"active": true,
"failure_count": 1,
"last_success_at": "<string>",
"last_failure_at": "<string>",
"created_at": "<string>",
"events": "<unknown>",
"line_ids": "<unknown>",
"secret": "<string>"
},
"trace_id": "<string>",
"request_id": "<string>"
}{
"success": false,
"data": "<unknown>",
"error": {
"status": 123,
"code": "<string>",
"message": "<string>",
"retry_after": 123,
"retry_at": "<string>",
"issues": [
{
"path": [
"<string>"
],
"message": "<string>",
"code": "<string>",
"min": 123,
"max": 123
}
]
},
"trace_id": "<string>",
"request_id": "<string>"
}{
"success": false,
"data": "<unknown>",
"error": {
"status": 123,
"code": "<string>",
"message": "<string>",
"retry_after": 123,
"retry_at": "<string>",
"issues": [
{
"path": [
"<string>"
],
"message": "<string>",
"code": "<string>",
"min": 123,
"max": 123
}
]
},
"trace_id": "<string>",
"request_id": "<string>"
}{
"success": false,
"data": "<unknown>",
"error": {
"status": 123,
"code": "<string>",
"message": "<string>",
"retry_after": 123,
"retry_at": "<string>",
"issues": [
{
"path": [
"<string>"
],
"message": "<string>",
"code": "<string>",
"min": 123,
"max": 123
}
]
},
"trace_id": "<string>",
"request_id": "<string>"
}{
"success": false,
"data": "<unknown>",
"error": {
"status": 123,
"code": "<string>",
"message": "<string>",
"retry_after": 123,
"retry_at": "<string>",
"issues": [
{
"path": [
"<string>"
],
"message": "<string>",
"code": "<string>",
"min": 123,
"max": 123
}
]
},
"trace_id": "<string>",
"request_id": "<string>"
}Create a webhook subscription
Creates a subscription for a URL and an event filter, and returns its signing secret once.
curl --request POST \
--url https://api.isnap.ai/v1/webhooks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"url": "https://hooks.your-domain.example/isnap",
"events": [
"message.received",
"line.*"
],
"line_ids": [
"line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1"
]
}
'import requests
url = "https://api.isnap.ai/v1/webhooks"
payload = {
"url": "https://hooks.your-domain.example/isnap",
"events": ["message.received", "line.*"],
"line_ids": ["line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1"]
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({
url: 'https://hooks.your-domain.example/isnap',
events: ['message.received', 'line.*'],
line_ids: ['line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1']
})
};
fetch('https://api.isnap.ai/v1/webhooks', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.isnap.ai/v1/webhooks",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://hooks.your-domain.example/isnap',
'events' => [
'message.received',
'line.*'
],
'line_ids' => [
'line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1'
]
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.isnap.ai/v1/webhooks"
payload := strings.NewReader("{\n \"url\": \"https://hooks.your-domain.example/isnap\",\n \"events\": [\n \"message.received\",\n \"line.*\"\n ],\n \"line_ids\": [\n \"line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1\"\n ]\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.isnap.ai/v1/webhooks")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"url\": \"https://hooks.your-domain.example/isnap\",\n \"events\": [\n \"message.received\",\n \"line.*\"\n ],\n \"line_ids\": [\n \"line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1\"\n ]\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.isnap.ai/v1/webhooks")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"url\": \"https://hooks.your-domain.example/isnap\",\n \"events\": [\n \"message.received\",\n \"line.*\"\n ],\n \"line_ids\": [\n \"line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1\"\n ]\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"id": "wh_3Kp8Rn9Fh2Lm5Qx7Vw1Yz4Bc6",
"url": "<string>",
"active": true,
"failure_count": 1,
"last_success_at": "<string>",
"last_failure_at": "<string>",
"created_at": "<string>",
"events": "<unknown>",
"line_ids": "<unknown>",
"secret": "<string>"
},
"trace_id": "<string>",
"request_id": "<string>"
}{
"success": false,
"data": "<unknown>",
"error": {
"status": 123,
"code": "<string>",
"message": "<string>",
"retry_after": 123,
"retry_at": "<string>",
"issues": [
{
"path": [
"<string>"
],
"message": "<string>",
"code": "<string>",
"min": 123,
"max": 123
}
]
},
"trace_id": "<string>",
"request_id": "<string>"
}{
"success": false,
"data": "<unknown>",
"error": {
"status": 123,
"code": "<string>",
"message": "<string>",
"retry_after": 123,
"retry_at": "<string>",
"issues": [
{
"path": [
"<string>"
],
"message": "<string>",
"code": "<string>",
"min": 123,
"max": 123
}
]
},
"trace_id": "<string>",
"request_id": "<string>"
}{
"success": false,
"data": "<unknown>",
"error": {
"status": 123,
"code": "<string>",
"message": "<string>",
"retry_after": 123,
"retry_at": "<string>",
"issues": [
{
"path": [
"<string>"
],
"message": "<string>",
"code": "<string>",
"min": 123,
"max": 123
}
]
},
"trace_id": "<string>",
"request_id": "<string>"
}{
"success": false,
"data": "<unknown>",
"error": {
"status": 123,
"code": "<string>",
"message": "<string>",
"retry_after": 123,
"retry_at": "<string>",
"issues": [
{
"path": [
"<string>"
],
"message": "<string>",
"code": "<string>",
"min": 123,
"max": 123
}
]
},
"trace_id": "<string>",
"request_id": "<string>"
}- Credential: an account API key. A trial key returns
401 unauthorized. - Line-scoped keys: the list of a line-scoped key is not applied here: the key can subscribe to any line of the account, or to all lines.
- Test keys: a subscription created with an
isnap_test_account key is a real subscription of the account, and it receives the events of every key: see Test keys. Idempotency-Key: not honored. The same request sent twice creates two subscriptions, and each event is then delivered twice to the URL. See Operations that honor the key.- Events: none. No event announces a new subscription: see What events do not announce.
- Secret: the response contains the signing secret: do not log it. This operation and
POST /v1/webhooks/{id}/rotate-secretare the only ones that return it. - Guide: Subscribing explains the URL rules, the event filter and the line filter.
Errors
| Status | error.code | When |
|---|---|---|
| 400 | validation_failed | url is missing or is not a URL. events is missing, is empty, or holds a name that is not an event type. line_ids is null, or one of its entries does not have the form of a line id. error.issues names each field. |
| 400 | webhook_url_not_allowed | The URL is refused. error.issues[0].code is invalid_url, not_https, ssrf_blocked or dns_failed: see URL rules. |
| 500 | internal_error | The URL is longer than 2,048 characters. Shorten it. |
Authorizations
An API key created in the iSnap dashboard, or a trial key, sent as Authorization: Bearer <key>. An API key starts with isnap_live_ or isnap_test_; both act on the same account, lines and billing. A trial key acts on its trial only.
Body
Public https:// URL that receives the deliveries, at most 2,048 characters: a longer URL returns 500 internal_error. A URL with another scheme, or whose host is or resolves to a non-public address, or does not resolve, is refused with 400 webhook_url_not_allowed.
"https://hooks.your-domain.example/isnap"
Event types to receive: exact names such as message.received, family wildcards such as line.*, or * for every event. At least one entry: there is no default. An exact name that is not an event type returns 400 validation_failed. Not every line.* event is sent for every kind of line: line.degraded and line.capability_changed are sent for BYOD lines only, and an outage shorter than 15 minutes on a line that is not a BYOD line sends neither line.offline nor line.connected.
1An exact event type such as message.received, a family wildcard such as line.*, or * for every event. A wildcard for a family that does not exist is accepted and matches nothing. webhook.test is never delivered through an event filter: iSnap sends it only to a suspended webhook subscription, to check whether its URL responds again.
message.queued, message.sent, message.delivered, message.read, message.failed, message.received, message.scheduled, message.cancelled, message.fallback_triggered, reaction.added, reaction.received, reaction.removed, line.connected, line.disconnected, line.offline, line.degraded, line.apple_id_flagged, line.quota_warning, line.quota_exceeded, line.capability_changed, line.terminated, line.variant_changed, line.variant_scheduled, line.variant_unscheduled, typing_indicator.started, typing_indicator.stopped, trial.linked, trial.dormant, trial.reactivated, trial.bind_conflict, trial.revoked, binding.released, pre_order.fulfilled, pre_order.cancelled, macos_session.rented, macos_session.released, webhook.test, *, message.*, reaction.*, line.*, typing_indicator.*, trial.*, binding.*, pre_order.*, macos_session.*, webhook.* ["message.received", "line.*"]
Lines to receive events for. Omitted or empty: every line, and the events tied to no line. With at least one id, the subscription receives only the events tied to one of those lines. The events of a trial are tied to the line the trial runs on: the subscription receives them only if the list contains its pool_line_id. Ids are checked for their format only: an id that matches no line is accepted and matches nothing.
Id of a line: line_ followed by 25 letters or digits.
^line_[0-9A-Za-z]{25}$["line_2k9Qp7RtY4mNb8VcXs3Wd6Lf1"]
Response
The webhook subscription, with its signing secret.
Always true on a success response.
true A webhook subscription, in the shape that POST /v1/webhooks and GET /v1/webhooks return.
Show child attributes
Show child attributes
Trace id of the request that produced this response: 32 lowercase hexadecimal characters. Quote it when you report a problem to iSnap.
Id of the request that produced this response: req_ followed by 25 letters or digits.