curl --request POST \
--url https://app.base44.com/api/v1/outbound-webhooks/endpoints \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"description": "Production receiver",
"selected_event_types": [
"app.created.v1",
"app.deleted.v1"
],
"target_url": "https://hooks.example.com/base44"
}
'import requests
url = "https://app.base44.com/api/v1/outbound-webhooks/endpoints"
payload = {
"description": "Production receiver",
"selected_event_types": ["app.created.v1", "app.deleted.v1"],
"target_url": "https://hooks.example.com/base44"
}
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({
description: 'Production receiver',
selected_event_types: ['app.created.v1', 'app.deleted.v1'],
target_url: 'https://hooks.example.com/base44'
})
};
fetch('https://app.base44.com/api/v1/outbound-webhooks/endpoints', 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://app.base44.com/api/v1/outbound-webhooks/endpoints",
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([
'description' => 'Production receiver',
'selected_event_types' => [
'app.created.v1',
'app.deleted.v1'
],
'target_url' => 'https://hooks.example.com/base44'
]),
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://app.base44.com/api/v1/outbound-webhooks/endpoints"
payload := strings.NewReader("{\n \"description\": \"Production receiver\",\n \"selected_event_types\": [\n \"app.created.v1\",\n \"app.deleted.v1\"\n ],\n \"target_url\": \"https://hooks.example.com/base44\"\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://app.base44.com/api/v1/outbound-webhooks/endpoints")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"description\": \"Production receiver\",\n \"selected_event_types\": [\n \"app.created.v1\",\n \"app.deleted.v1\"\n ],\n \"target_url\": \"https://hooks.example.com/base44\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/v1/outbound-webhooks/endpoints")
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 \"description\": \"Production receiver\",\n \"selected_event_types\": [\n \"app.created.v1\",\n \"app.deleted.v1\"\n ],\n \"target_url\": \"https://hooks.example.com/base44\"\n}"
response = http.request(request)
puts response.read_body{
"activated": true,
"detail": "",
"endpoint": {
"activated_at": "2026-10-04T19:25:39.423Z",
"consecutive_failed_deliveries": 0,
"created_date": "2026-10-04T19:25:34.260Z",
"description": "Provisioning",
"health": "healthy",
"id": "6ac2a82edfafd1b6c6a9c27d",
"last_success_at": "2026-10-05T13:23:36.743Z",
"selected_event_types": [
"app.created.v1",
"app.deleted.v1"
],
"state": "active",
"target_url": "https://hooks.example.com/base44"
},
"http_status": 200
}{
"error": {
"code": "invalid_request",
"details": {
"fields": [
{
"field": "target_url",
"problem": "Field required"
}
]
},
"message": "The request is invalid."
}
}{
"error": {
"code": "unauthenticated",
"details": {},
"message": "Invalid workspace API key."
}
}{
"error": {
"code": "permission_denied",
"details": {},
"message": "This workspace API key is not permitted to manage outbound webhooks. Needed: [outbound_webhooks:write]"
}
}{
"error": {
"code": "not_found",
"details": {},
"message": "Webhook endpoint not found."
}
}{
"error": {
"code": "limit_exceeded",
"details": {
"max_endpoints": 10
},
"message": "A workspace may have at most 10 webhook endpoints."
}
}{
"error": {
"code": "rate_limited",
"details": {
"retry_after_seconds": 42
},
"message": "Rate limit exceeded. Retry after the interval in the Retry-After header."
}
}{
"error": {
"code": "internal_error",
"details": {},
"message": "An internal error occurred. Retry with exponential backoff."
}
}Register an endpoint
Registers an endpoint and tests it in the same call. The test is a signed webhook.test.v1 event. The endpoint activates only if it answers with a 2xx status within 5 seconds and returns the event’s data.challenge in its response body. activated says whether it did; when it didn’t, failure_category, http_status and detail say why. The response is 201 either way, because the endpoint was created: retry a failed test with POST /endpoints/{endpoint_id}/test, not by registering again. Registering isn’t idempotent: if you lose the response, list the endpoints before retrying. A workspace can have up to 10 endpoints.
curl --request POST \
--url https://app.base44.com/api/v1/outbound-webhooks/endpoints \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"description": "Production receiver",
"selected_event_types": [
"app.created.v1",
"app.deleted.v1"
],
"target_url": "https://hooks.example.com/base44"
}
'import requests
url = "https://app.base44.com/api/v1/outbound-webhooks/endpoints"
payload = {
"description": "Production receiver",
"selected_event_types": ["app.created.v1", "app.deleted.v1"],
"target_url": "https://hooks.example.com/base44"
}
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({
description: 'Production receiver',
selected_event_types: ['app.created.v1', 'app.deleted.v1'],
target_url: 'https://hooks.example.com/base44'
})
};
fetch('https://app.base44.com/api/v1/outbound-webhooks/endpoints', 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://app.base44.com/api/v1/outbound-webhooks/endpoints",
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([
'description' => 'Production receiver',
'selected_event_types' => [
'app.created.v1',
'app.deleted.v1'
],
'target_url' => 'https://hooks.example.com/base44'
]),
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://app.base44.com/api/v1/outbound-webhooks/endpoints"
payload := strings.NewReader("{\n \"description\": \"Production receiver\",\n \"selected_event_types\": [\n \"app.created.v1\",\n \"app.deleted.v1\"\n ],\n \"target_url\": \"https://hooks.example.com/base44\"\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://app.base44.com/api/v1/outbound-webhooks/endpoints")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"description\": \"Production receiver\",\n \"selected_event_types\": [\n \"app.created.v1\",\n \"app.deleted.v1\"\n ],\n \"target_url\": \"https://hooks.example.com/base44\"\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://app.base44.com/api/v1/outbound-webhooks/endpoints")
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 \"description\": \"Production receiver\",\n \"selected_event_types\": [\n \"app.created.v1\",\n \"app.deleted.v1\"\n ],\n \"target_url\": \"https://hooks.example.com/base44\"\n}"
response = http.request(request)
puts response.read_body{
"activated": true,
"detail": "",
"endpoint": {
"activated_at": "2026-10-04T19:25:39.423Z",
"consecutive_failed_deliveries": 0,
"created_date": "2026-10-04T19:25:34.260Z",
"description": "Provisioning",
"health": "healthy",
"id": "6ac2a82edfafd1b6c6a9c27d",
"last_success_at": "2026-10-05T13:23:36.743Z",
"selected_event_types": [
"app.created.v1",
"app.deleted.v1"
],
"state": "active",
"target_url": "https://hooks.example.com/base44"
},
"http_status": 200
}{
"error": {
"code": "invalid_request",
"details": {
"fields": [
{
"field": "target_url",
"problem": "Field required"
}
]
},
"message": "The request is invalid."
}
}{
"error": {
"code": "unauthenticated",
"details": {},
"message": "Invalid workspace API key."
}
}{
"error": {
"code": "permission_denied",
"details": {},
"message": "This workspace API key is not permitted to manage outbound webhooks. Needed: [outbound_webhooks:write]"
}
}{
"error": {
"code": "not_found",
"details": {},
"message": "Webhook endpoint not found."
}
}{
"error": {
"code": "limit_exceeded",
"details": {
"max_endpoints": 10
},
"message": "A workspace may have at most 10 webhook endpoints."
}
}{
"error": {
"code": "rate_limited",
"details": {
"retry_after_seconds": 42
},
"message": "Rate limit exceeded. Retry after the interval in the Retry-After header."
}
}{
"error": {
"code": "internal_error",
"details": {},
"message": "An internal error occurred. Retry with exponential backoff."
}
}Authorizations
A workspace API key with the outbound_webhooks:write scope, sent as Authorization: Bearer <key>.
Body
The event types to send, such as app.created.v1. Each type is a separate version.
1 - 64 elementsThe HTTPS URL that receives events. It must resolve to public addresses, can't contain credentials, and must answer directly: redirects aren't followed.
2048A note for your own reference, up to 200 characters.
200Response
Successful Response
The endpoint and the result of its activation test.
Whether the endpoint passed the test and is active.
Show child attributes
Show child attributes
{
"activated_at": "2026-10-04T19:25:39.423Z",
"consecutive_failed_deliveries": 0,
"created_date": "2026-10-04T19:25:34.260Z",
"description": "Provisioning",
"health": "healthy",
"id": "6ac2a82edfafd1b6c6a9c27d",
"last_success_at": "2026-10-05T13:23:36.743Z",
"selected_event_types": ["app.created.v1", "app.deleted.v1"],
"state": "active",
"target_url": "https://hooks.example.com/base44"
}
A sentence explaining the result. Not a stable contract.
Why the test failed: timeout, transport (no connection), server_error (5xx), client_error (4xx, or a 2xx without the challenge), redirect, ssrf_rejected (the URL resolved to a non-public address) or internal. Null on success.
The status your endpoint answered with. Null when it didn't answer.
Was this page helpful?