Webhook endpoints
The HTTPS URLs where Monerail sends events. Up to 10 per business, test and live together. See Webhooks to verify signatures.
/v1/webhook_endpointsCreate a webhook endpointGET/v1/webhook_endpoints/{id}Retrieve a webhook endpointGET/v1/webhook_endpointsList webhook endpointsDELETE/v1/webhook_endpoints/{id}Delete a webhook endpointThe webhook endpoint object
Attributes
Unique identifier (UUID).
true for live objects, false for test objects.
Always webhook_endpoint.
HTTPS URL that receives the events.
Subscribed event types. * means all, payout.* all payout events.
enableddisabledSigning secret (whsec_…). Only in the original 201 response to the creation, never afterwards.
Creation time, ISO 8601 (UTC).
{
"id": "9a661b6d-2895-414e-b1d1-51e473582cbb",
"object": "webhook_endpoint",
"livemode": false,
"url": "https://example.com/webhooks/monerail",
"events": [
"collection.succeeded",
"payout.*"
],
"status": "enabled",
"created_at": "2026-10-30T13:47:06.394Z"
}Create a webhook endpoint
The 201 response includes the signing secret. Store it: it is never returned again, not even when the request is replayed with the same Idempotency-Key. If you lose it, delete the endpoint and create a new one. Live endpoints need an approved business verification.
Key roles: Owner or Developer
Body
HTTPS URL, up to 500 characters, without user:password@. The host must be a domain name: IP addresses are refused.
Event types to receive: exact types, prefix.* or *.
Returns
Returns the new webhook endpoint object with 201.
Errors worth handling
| HTTP | Code | Description |
|---|---|---|
400 | url_ | Not an HTTPS URL with a domain name, or it contains user:password@. IP addresses are refused. |
400 | parameter_ | A parameter has the wrong type or value. See param. |
400 | limit_ | Your business already has 10 webhook endpoints (test and live together). |
403 | permission_ | The key’s creator does not have the role required for this operation. |
curl https://api.monerail.com/v1/webhook_endpoints \
-H "Authorization: Bearer $MONERAIL_KEY" \
-H "Content-Type: application/json" \
-d '{
"url": "https://example.com/webhooks/monerail",
"events": [
"collection.succeeded",
"payout.*"
]
}'const res = await fetch('https://api.monerail.com/v1/webhook_endpoints', {
method: 'POST',
headers: {
Authorization: `Bearer ${process.env.MONERAIL_KEY}`,
'Content-Type': 'application/json',
},
body: JSON.stringify({
url: 'https://example.com/webhooks/monerail',
events: ['collection.succeeded', 'payout.*'],
}),
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
console.log(data);import os
import requests
res = requests.post(
"https://api.monerail.com/v1/webhook_endpoints",
headers={"Authorization": f"Bearer {os.environ['MONERAIL_KEY']}"},
json={
"url": "https://example.com/webhooks/monerail",
"events": ["collection.succeeded", "payout.*"],
},
timeout=30,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
print(data)<?php
$ch = curl_init('https://api.monerail.com/v1/webhook_endpoints');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('MONERAIL_KEY'),
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'url' => 'https://example.com/webhooks/monerail',
'events' => ['collection.succeeded', 'payout.*'],
]),
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
throw new RuntimeException($data['error']['code'] . ': ' . $data['error']['message']);
}
print_r($data);package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
payload, err := json.Marshal(map[string]any{
"url": "https://example.com/webhooks/monerail",
"events": []string{"collection.succeeded", "payout.*"},
})
if err != nil {
panic(err)
}
req, err := http.NewRequest(http.MethodPost, "https://api.monerail.com/v1/webhook_endpoints", bytes.NewReader(payload))
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("MONERAIL_KEY"))
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
if res.StatusCode >= 400 {
panic(fmt.Sprint(data["error"]))
}
fmt.Println(data)
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
String body = """
{
"url": "https://example.com/webhooks/monerail",
"events": [
"collection.succeeded",
"payout.*"
]
}
""";
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.monerail.com/v1/webhook_endpoints"))
.header("Authorization", "Bearer " + System.getenv("MONERAIL_KEY"))
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(body))
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new RuntimeException(response.body());
}
System.out.println(response.body()); // parse with your JSON library (Jackson, Gson…)
}
}require "json"
require "net/http"
uri = URI("https://api.monerail.com/v1/webhook_endpoints")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("MONERAIL_KEY")}"
req["Content-Type"] = "application/json"
req.body = JSON.generate({
url: "https://example.com/webhooks/monerail",
events: ["collection.succeeded", "payout.*"],
})
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
raise "#{data["error"]["code"]}: #{data["error"]["message"]}" unless res.is_a?(Net::HTTPSuccess)
puts datausing System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("MONERAIL_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Post, "https://api.monerail.com/v1/webhook_endpoints");
request.Content = JsonContent.Create(new
{
url = "https://example.com/webhooks/monerail",
events = new[] { "collection.succeeded", "payout.*" },
});
using var response = await http.SendAsync(request);
var data = await response.Content.ReadFromJsonAsync<JsonElement>();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(data.GetProperty("error").GetProperty("message").GetString());
}
Console.WriteLine(data);{
"id": "9a661b6d-2895-414e-b1d1-51e473582cbb",
"object": "webhook_endpoint",
"livemode": false,
"url": "https://example.com/webhooks/monerail",
"events": [
"collection.succeeded",
"payout.*"
],
"status": "enabled",
"created_at": "2026-10-30T13:47:06.394Z",
"secret": "whsec_9b89c8bdc0b89f67637e017c49f9539d68b2132ea7492041"
}Retrieve a webhook endpoint
The secret is not included.
Key roles: Owner or Developer
Path parameters
ID of the endpoint.
Returns
Returns the webhook endpoint object.
Errors worth handling
| HTTP | Code | Description |
|---|---|---|
403 | permission_ | The key’s creator does not have the role required for this operation. |
404 | resource_ | No object with this ID in this mode. |
curl https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb \
-H "Authorization: Bearer $MONERAIL_KEY"const res = await fetch('https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb', {
headers: {
Authorization: `Bearer ${process.env.MONERAIL_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
console.log(data);import os
import requests
res = requests.get(
"https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb",
headers={"Authorization": f"Bearer {os.environ['MONERAIL_KEY']}"},
timeout=30,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
print(data)<?php
$ch = curl_init('https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('MONERAIL_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
throw new RuntimeException($data['error']['code'] . ': ' . $data['error']['message']);
}
print_r($data);package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest(http.MethodGet, "https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("MONERAIL_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
if res.StatusCode >= 400 {
panic(fmt.Sprint(data["error"]))
}
fmt.Println(data)
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb"))
.header("Authorization", "Bearer " + System.getenv("MONERAIL_KEY"))
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new RuntimeException(response.body());
}
System.out.println(response.body()); // parse with your JSON library (Jackson, Gson…)
}
}require "json"
require "net/http"
uri = URI("https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("MONERAIL_KEY")}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
raise "#{data["error"]["code"]}: #{data["error"]["message"]}" unless res.is_a?(Net::HTTPSuccess)
puts datausing System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("MONERAIL_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Get, "https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb");
using var response = await http.SendAsync(request);
var data = await response.Content.ReadFromJsonAsync<JsonElement>();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(data.GetProperty("error").GetProperty("message").GetString());
}
Console.WriteLine(data);{
"id": "9a661b6d-2895-414e-b1d1-51e473582cbb",
"object": "webhook_endpoint",
"livemode": false,
"url": "https://example.com/webhooks/monerail",
"events": [
"collection.succeeded",
"payout.*"
],
"status": "enabled",
"created_at": "2026-10-30T13:47:06.394Z"
}List webhook endpoints
Newest first. Test and live endpoints are listed separately, by the mode of the key.
Key roles: Owner or Developer
Query parameters
20Number of objects to return, 1 to 100.
Cursor: the ID of the last object of the previous page.
Returns
Returns a list object: data holds up to limit webhook_endpoint objects, and has_more tells whether more follow.
Errors worth handling
| HTTP | Code | Description |
|---|---|---|
403 | permission_ | The key’s creator does not have the role required for this operation. |
curl "https://api.monerail.com/v1/webhook_endpoints?limit=10" \
-H "Authorization: Bearer $MONERAIL_KEY"const res = await fetch('https://api.monerail.com/v1/webhook_endpoints?limit=10', {
headers: {
Authorization: `Bearer ${process.env.MONERAIL_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
console.log(data);import os
import requests
res = requests.get(
"https://api.monerail.com/v1/webhook_endpoints",
headers={"Authorization": f"Bearer {os.environ['MONERAIL_KEY']}"},
params={"limit": 10},
timeout=30,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
print(data)<?php
$ch = curl_init('https://api.monerail.com/v1/webhook_endpoints?limit=10');
curl_setopt_array($ch, [
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('MONERAIL_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
throw new RuntimeException($data['error']['code'] . ': ' . $data['error']['message']);
}
print_r($data);package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest(http.MethodGet, "https://api.monerail.com/v1/webhook_endpoints?limit=10", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("MONERAIL_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
if res.StatusCode >= 400 {
panic(fmt.Sprint(data["error"]))
}
fmt.Println(data)
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.monerail.com/v1/webhook_endpoints?limit=10"))
.header("Authorization", "Bearer " + System.getenv("MONERAIL_KEY"))
.GET()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new RuntimeException(response.body());
}
System.out.println(response.body()); // parse with your JSON library (Jackson, Gson…)
}
}require "json"
require "net/http"
uri = URI("https://api.monerail.com/v1/webhook_endpoints?limit=10")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("MONERAIL_KEY")}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
raise "#{data["error"]["code"]}: #{data["error"]["message"]}" unless res.is_a?(Net::HTTPSuccess)
puts datausing System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("MONERAIL_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Get, "https://api.monerail.com/v1/webhook_endpoints?limit=10");
using var response = await http.SendAsync(request);
var data = await response.Content.ReadFromJsonAsync<JsonElement>();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(data.GetProperty("error").GetProperty("message").GetString());
}
Console.WriteLine(data);{
"object": "list",
"url": "/v1/webhook_endpoints",
"data": [
{
"id": "9a661b6d-2895-414e-b1d1-51e473582cbb",
"object": "webhook_endpoint",
"livemode": false,
"url": "https://example.com/webhooks/monerail",
"events": [
"collection.succeeded",
"payout.*"
],
"status": "enabled",
"created_at": "2026-10-30T13:47:06.394Z"
}
],
"has_more": false
}Delete a webhook endpoint
Stops all deliveries to this URL, including pending retries.
Key roles: Owner or Developer
Path parameters
ID of the endpoint.
Returns
Returns { id, object, deleted: true }.
Errors worth handling
| HTTP | Code | Description |
|---|---|---|
403 | permission_ | The key’s creator does not have the role required for this operation. |
404 | resource_ | No object with this ID in this mode. |
curl -X DELETE https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb \
-H "Authorization: Bearer $MONERAIL_KEY"const res = await fetch('https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb', {
method: 'DELETE',
headers: {
Authorization: `Bearer ${process.env.MONERAIL_KEY}`,
},
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error.code}: ${data.error.message}`);
console.log(data);import os
import requests
res = requests.delete(
"https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb",
headers={"Authorization": f"Bearer {os.environ['MONERAIL_KEY']}"},
timeout=30,
)
data = res.json()
if not res.ok:
raise RuntimeError(f"{data['error']['code']}: {data['error']['message']}")
print(data)<?php
$ch = curl_init('https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb');
curl_setopt_array($ch, [
CURLOPT_CUSTOMREQUEST => 'DELETE',
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . getenv('MONERAIL_KEY'),
],
CURLOPT_RETURNTRANSFER => true,
]);
$data = json_decode(curl_exec($ch), true);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
if ($status >= 400) {
throw new RuntimeException($data['error']['code'] . ': ' . $data['error']['message']);
}
print_r($data);package main
import (
"encoding/json"
"fmt"
"net/http"
"os"
)
func main() {
req, err := http.NewRequest(http.MethodDelete, "https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb", nil)
if err != nil {
panic(err)
}
req.Header.Set("Authorization", "Bearer "+os.Getenv("MONERAIL_KEY"))
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
var data map[string]any
if err := json.NewDecoder(res.Body).Decode(&data); err != nil {
panic(err)
}
if res.StatusCode >= 400 {
panic(fmt.Sprint(data["error"]))
}
fmt.Println(data)
}import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
public class Example {
public static void main(String[] args) throws Exception {
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb"))
.header("Authorization", "Bearer " + System.getenv("MONERAIL_KEY"))
.DELETE()
.build();
HttpResponse<String> response = HttpClient.newHttpClient()
.send(request, HttpResponse.BodyHandlers.ofString());
if (response.statusCode() >= 400) {
throw new RuntimeException(response.body());
}
System.out.println(response.body()); // parse with your JSON library (Jackson, Gson…)
}
}require "json"
require "net/http"
uri = URI("https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer #{ENV.fetch("MONERAIL_KEY")}"
res = Net::HTTP.start(uri.host, uri.port, use_ssl: true) { |http| http.request(req) }
data = JSON.parse(res.body)
raise "#{data["error"]["code"]}: #{data["error"]["message"]}" unless res.is_a?(Net::HTTPSuccess)
puts datausing System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
using var http = new HttpClient();
http.DefaultRequestHeaders.Authorization =
new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("MONERAIL_KEY"));
using var request = new HttpRequestMessage(HttpMethod.Delete, "https://api.monerail.com/v1/webhook_endpoints/9a661b6d-2895-414e-b1d1-51e473582cbb");
using var response = await http.SendAsync(request);
var data = await response.Content.ReadFromJsonAsync<JsonElement>();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException(data.GetProperty("error").GetProperty("message").GetString());
}
Console.WriteLine(data);{
"id": "9a661b6d-2895-414e-b1d1-51e473582cbb",
"object": "webhook_endpoint",
"deleted": true
}