curl --request POST \
--url https://api.spenza.com/api/v1.1/webhooks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"operator": "SpenzaJ",
"network": "T-Mobile",
"authentication": {
"username": "<string>",
"password": "<string>",
"token": "<string>",
"apiKey": "<string>",
"apiKeyHeader": "X-API-Key"
},
"webhookUrls": {
"messageUrl": "<string>",
"callbackMessageUrl": "<string>",
"voiceUrl": "<string>",
"callbackVoiceUrl": "<string>",
"voiceStreamUrl": "<string>"
},
"retryPolicy": {
"maxAttempts": 3,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"status": "active",
"description": "<string>",
"update": false
}
'import requests
url = "https://api.spenza.com/api/v1.1/webhooks"
payload = {
"operator": "SpenzaJ",
"network": "T-Mobile",
"authentication": {
"username": "<string>",
"password": "<string>",
"token": "<string>",
"apiKey": "<string>",
"apiKeyHeader": "X-API-Key"
},
"webhookUrls": {
"messageUrl": "<string>",
"callbackMessageUrl": "<string>",
"voiceUrl": "<string>",
"callbackVoiceUrl": "<string>",
"voiceStreamUrl": "<string>"
},
"retryPolicy": {
"maxAttempts": 3,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"status": "active",
"description": "<string>",
"update": False
}
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({
operator: 'SpenzaJ',
network: 'T-Mobile',
authentication: {
username: '<string>',
password: '<string>',
token: '<string>',
apiKey: '<string>',
apiKeyHeader: 'X-API-Key'
},
webhookUrls: {
messageUrl: '<string>',
callbackMessageUrl: '<string>',
voiceUrl: '<string>',
callbackVoiceUrl: '<string>',
voiceStreamUrl: '<string>'
},
retryPolicy: {
maxAttempts: 3,
backoffStrategy: 'exponential',
initialDelaySeconds: 5,
maxDelaySeconds: 300
},
status: 'active',
description: '<string>',
update: false
})
};
fetch('https://api.spenza.com/api/v1.1/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.spenza.com/api/v1.1/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([
'operator' => 'SpenzaJ',
'network' => 'T-Mobile',
'authentication' => [
'username' => '<string>',
'password' => '<string>',
'token' => '<string>',
'apiKey' => '<string>',
'apiKeyHeader' => 'X-API-Key'
],
'webhookUrls' => [
'messageUrl' => '<string>',
'callbackMessageUrl' => '<string>',
'voiceUrl' => '<string>',
'callbackVoiceUrl' => '<string>',
'voiceStreamUrl' => '<string>'
],
'retryPolicy' => [
'maxAttempts' => 3,
'backoffStrategy' => 'exponential',
'initialDelaySeconds' => 5,
'maxDelaySeconds' => 300
],
'status' => 'active',
'description' => '<string>',
'update' => false
]),
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.spenza.com/api/v1.1/webhooks"
payload := strings.NewReader("{\n \"operator\": \"SpenzaJ\",\n \"network\": \"T-Mobile\",\n \"authentication\": {\n \"username\": \"<string>\",\n \"password\": \"<string>\",\n \"token\": \"<string>\",\n \"apiKey\": \"<string>\",\n \"apiKeyHeader\": \"X-API-Key\"\n },\n \"webhookUrls\": {\n \"messageUrl\": \"<string>\",\n \"callbackMessageUrl\": \"<string>\",\n \"voiceUrl\": \"<string>\",\n \"callbackVoiceUrl\": \"<string>\",\n \"voiceStreamUrl\": \"<string>\"\n },\n \"retryPolicy\": {\n \"maxAttempts\": 3,\n \"backoffStrategy\": \"exponential\",\n \"initialDelaySeconds\": 5,\n \"maxDelaySeconds\": 300\n },\n \"status\": \"active\",\n \"description\": \"<string>\",\n \"update\": false\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.spenza.com/api/v1.1/webhooks")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"operator\": \"SpenzaJ\",\n \"network\": \"T-Mobile\",\n \"authentication\": {\n \"username\": \"<string>\",\n \"password\": \"<string>\",\n \"token\": \"<string>\",\n \"apiKey\": \"<string>\",\n \"apiKeyHeader\": \"X-API-Key\"\n },\n \"webhookUrls\": {\n \"messageUrl\": \"<string>\",\n \"callbackMessageUrl\": \"<string>\",\n \"voiceUrl\": \"<string>\",\n \"callbackVoiceUrl\": \"<string>\",\n \"voiceStreamUrl\": \"<string>\"\n },\n \"retryPolicy\": {\n \"maxAttempts\": 3,\n \"backoffStrategy\": \"exponential\",\n \"initialDelaySeconds\": 5,\n \"maxDelaySeconds\": 300\n },\n \"status\": \"active\",\n \"description\": \"<string>\",\n \"update\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.spenza.com/api/v1.1/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 \"operator\": \"SpenzaJ\",\n \"network\": \"T-Mobile\",\n \"authentication\": {\n \"username\": \"<string>\",\n \"password\": \"<string>\",\n \"token\": \"<string>\",\n \"apiKey\": \"<string>\",\n \"apiKeyHeader\": \"X-API-Key\"\n },\n \"webhookUrls\": {\n \"messageUrl\": \"<string>\",\n \"callbackMessageUrl\": \"<string>\",\n \"voiceUrl\": \"<string>\",\n \"callbackVoiceUrl\": \"<string>\",\n \"voiceStreamUrl\": \"<string>\"\n },\n \"retryPolicy\": {\n \"maxAttempts\": 3,\n \"backoffStrategy\": \"exponential\",\n \"initialDelaySeconds\": 5,\n \"maxDelaySeconds\": 300\n },\n \"status\": \"active\",\n \"description\": \"<string>\",\n \"update\": false\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"id": "wh_69146d70ed68",
"operator": "SpenzaJ",
"network": "T-Mobile",
"eventType": "sms",
"status": "active",
"authentication": {
"type": "bearer",
"token": "***ENCRYPTED***"
},
"webhookUrls": {
"messageUrl": "https://partner.example.com/hooks/sms",
"callbackMessageUrl": null,
"voiceUrl": null,
"callbackVoiceUrl": null,
"voiceStreamUrl": null
},
"retryPolicy": {
"maxAttempts": 5,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"description": null,
"createdAt": "2026-07-10T10:00:00.000Z",
"updatedAt": "2026-07-10T10:00:00.000Z",
"signingSecret": "whsec_9b1f8c2e7a4d6053b8e1f2a9c4d7e6053b8e1f2a9c4d7e6053b8e1f2a9c4d70"
}
}Register or update a webhook
Register a webhook for operator SMS/voice events. At most one
registration per (account, operator, network) — eventType is not
part of that key, so registering a second event type for the same
operator+network replaces the first. Registering again for an existing
combination without update: true is a 409.
eventType: voice (or both) registers successfully as long as the
operator/network combination itself is valid — there is no
registration-time check that voice is actually enabled, unlike sms.
Voice events only fire for a line whose active plan includes voice
support; a line on a plan with no voice allowance won’t produce voice
events even with a registered webhook.
signingSecret is returned only from this creation path (format
whsec_<64 hex chars>) — no read endpoint ever returns it again. Store
it immediately; losing it means re-registering. Use it to verify the
X-Spenza-Signature header on every delivery (see the Webhooks guide).
curl --request POST \
--url https://api.spenza.com/api/v1.1/webhooks \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"operator": "SpenzaJ",
"network": "T-Mobile",
"authentication": {
"username": "<string>",
"password": "<string>",
"token": "<string>",
"apiKey": "<string>",
"apiKeyHeader": "X-API-Key"
},
"webhookUrls": {
"messageUrl": "<string>",
"callbackMessageUrl": "<string>",
"voiceUrl": "<string>",
"callbackVoiceUrl": "<string>",
"voiceStreamUrl": "<string>"
},
"retryPolicy": {
"maxAttempts": 3,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"status": "active",
"description": "<string>",
"update": false
}
'import requests
url = "https://api.spenza.com/api/v1.1/webhooks"
payload = {
"operator": "SpenzaJ",
"network": "T-Mobile",
"authentication": {
"username": "<string>",
"password": "<string>",
"token": "<string>",
"apiKey": "<string>",
"apiKeyHeader": "X-API-Key"
},
"webhookUrls": {
"messageUrl": "<string>",
"callbackMessageUrl": "<string>",
"voiceUrl": "<string>",
"callbackVoiceUrl": "<string>",
"voiceStreamUrl": "<string>"
},
"retryPolicy": {
"maxAttempts": 3,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"status": "active",
"description": "<string>",
"update": False
}
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({
operator: 'SpenzaJ',
network: 'T-Mobile',
authentication: {
username: '<string>',
password: '<string>',
token: '<string>',
apiKey: '<string>',
apiKeyHeader: 'X-API-Key'
},
webhookUrls: {
messageUrl: '<string>',
callbackMessageUrl: '<string>',
voiceUrl: '<string>',
callbackVoiceUrl: '<string>',
voiceStreamUrl: '<string>'
},
retryPolicy: {
maxAttempts: 3,
backoffStrategy: 'exponential',
initialDelaySeconds: 5,
maxDelaySeconds: 300
},
status: 'active',
description: '<string>',
update: false
})
};
fetch('https://api.spenza.com/api/v1.1/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.spenza.com/api/v1.1/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([
'operator' => 'SpenzaJ',
'network' => 'T-Mobile',
'authentication' => [
'username' => '<string>',
'password' => '<string>',
'token' => '<string>',
'apiKey' => '<string>',
'apiKeyHeader' => 'X-API-Key'
],
'webhookUrls' => [
'messageUrl' => '<string>',
'callbackMessageUrl' => '<string>',
'voiceUrl' => '<string>',
'callbackVoiceUrl' => '<string>',
'voiceStreamUrl' => '<string>'
],
'retryPolicy' => [
'maxAttempts' => 3,
'backoffStrategy' => 'exponential',
'initialDelaySeconds' => 5,
'maxDelaySeconds' => 300
],
'status' => 'active',
'description' => '<string>',
'update' => false
]),
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.spenza.com/api/v1.1/webhooks"
payload := strings.NewReader("{\n \"operator\": \"SpenzaJ\",\n \"network\": \"T-Mobile\",\n \"authentication\": {\n \"username\": \"<string>\",\n \"password\": \"<string>\",\n \"token\": \"<string>\",\n \"apiKey\": \"<string>\",\n \"apiKeyHeader\": \"X-API-Key\"\n },\n \"webhookUrls\": {\n \"messageUrl\": \"<string>\",\n \"callbackMessageUrl\": \"<string>\",\n \"voiceUrl\": \"<string>\",\n \"callbackVoiceUrl\": \"<string>\",\n \"voiceStreamUrl\": \"<string>\"\n },\n \"retryPolicy\": {\n \"maxAttempts\": 3,\n \"backoffStrategy\": \"exponential\",\n \"initialDelaySeconds\": 5,\n \"maxDelaySeconds\": 300\n },\n \"status\": \"active\",\n \"description\": \"<string>\",\n \"update\": false\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.spenza.com/api/v1.1/webhooks")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"operator\": \"SpenzaJ\",\n \"network\": \"T-Mobile\",\n \"authentication\": {\n \"username\": \"<string>\",\n \"password\": \"<string>\",\n \"token\": \"<string>\",\n \"apiKey\": \"<string>\",\n \"apiKeyHeader\": \"X-API-Key\"\n },\n \"webhookUrls\": {\n \"messageUrl\": \"<string>\",\n \"callbackMessageUrl\": \"<string>\",\n \"voiceUrl\": \"<string>\",\n \"callbackVoiceUrl\": \"<string>\",\n \"voiceStreamUrl\": \"<string>\"\n },\n \"retryPolicy\": {\n \"maxAttempts\": 3,\n \"backoffStrategy\": \"exponential\",\n \"initialDelaySeconds\": 5,\n \"maxDelaySeconds\": 300\n },\n \"status\": \"active\",\n \"description\": \"<string>\",\n \"update\": false\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.spenza.com/api/v1.1/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 \"operator\": \"SpenzaJ\",\n \"network\": \"T-Mobile\",\n \"authentication\": {\n \"username\": \"<string>\",\n \"password\": \"<string>\",\n \"token\": \"<string>\",\n \"apiKey\": \"<string>\",\n \"apiKeyHeader\": \"X-API-Key\"\n },\n \"webhookUrls\": {\n \"messageUrl\": \"<string>\",\n \"callbackMessageUrl\": \"<string>\",\n \"voiceUrl\": \"<string>\",\n \"callbackVoiceUrl\": \"<string>\",\n \"voiceStreamUrl\": \"<string>\"\n },\n \"retryPolicy\": {\n \"maxAttempts\": 3,\n \"backoffStrategy\": \"exponential\",\n \"initialDelaySeconds\": 5,\n \"maxDelaySeconds\": 300\n },\n \"status\": \"active\",\n \"description\": \"<string>\",\n \"update\": false\n}"
response = http.request(request)
puts response.read_body{
"success": true,
"data": {
"id": "wh_69146d70ed68",
"operator": "SpenzaJ",
"network": "T-Mobile",
"eventType": "sms",
"status": "active",
"authentication": {
"type": "bearer",
"token": "***ENCRYPTED***"
},
"webhookUrls": {
"messageUrl": "https://partner.example.com/hooks/sms",
"callbackMessageUrl": null,
"voiceUrl": null,
"callbackVoiceUrl": null,
"voiceStreamUrl": null
},
"retryPolicy": {
"maxAttempts": 5,
"backoffStrategy": "exponential",
"initialDelaySeconds": 5,
"maxDelaySeconds": 300
},
"description": null,
"createdAt": "2026-07-10T10:00:00.000Z",
"updatedAt": "2026-07-10T10:00:00.000Z",
"signingSecret": "whsec_9b1f8c2e7a4d6053b8e1f2a9c4d7e6053b8e1f2a9c4d7e6053b8e1f2a9c4d70"
}
}Authorizations
Bearer token obtained from POST /api/v1.1/auth/token.
Body
"SpenzaJ"
"T-Mobile"
sms, voice, both Credential your endpoint requires — Spenza presents it when calling you. This is not a payload-signing scheme (see signingSecret/X-Spenza-Signature for that).
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
Show child attributes
active, inactive Set true to overwrite an existing registration for this operator+network. Coerced via class-transformer — sending the string "false" is truthy and also overwrites; send a real JSON boolean.
Response
Registered (or updated, if update:true).

