Subscribe a URL to the events you care about.
Body
Body| Field | Type | Description |
|---|
endpoint* | string | An absolute https URL. Redirects are never followed, so give the final one. |
events* | string[] | At least one event type. Subscribing to a type whose producer lands later is allowed and simply never fires. |
curl -X POST "https://api.rasket.com/webhooks" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0" \
-H "Content-Type: application/json" \
-d '{
"endpoint": "https://acme.example.com/hooks/rasket",
"events": ["email.delivered", "email.bounced", "email.complained"]
}'
const response = await fetch("https://api.rasket.com/webhooks", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
"Content-Type": "application/json",
},
body: JSON.stringify({
endpoint: "https://acme.example.com/hooks/rasket",
events: ["email.delivered", "email.bounced", "email.complained"]
}),
});
const { id } = await response.json();
import os
import requests
response = requests.post(
"https://api.rasket.com/webhooks",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
json={
"endpoint": "https://acme.example.com/hooks/rasket",
"events": ["email.delivered", "email.bounced", "email.complained"]
},
)
id = response.json()["id"]
Response 201
{
"object": "webhook",
"id": "wh_3b7f21c0d9",
"endpoint": "https://acme.example.com/hooks/rasket",
"events": ["email.delivered", "email.bounced", "email.complained"],
"status": "enabled",
"signing_secret": "whsec_rq0Yc2m9Xn4bZ1sK7wLp3vTf",
"created_at": "2026-09-09T09:20:31.004Z"
}
signing_secret is returned by this response and never again in full. Every later read shows it masked.- The URL is checked for reachability and against blocked address ranges when you create it, and again on every delivery.
Every endpoint on the team.
Query parameters
Query parameters| Field | Type | Description |
|---|
limit | integer | How many items to return, 1–100. Defaults to 20. |
after | string | Return the page that follows this item ID. Mutually exclusive with before. |
before | string | Return the page that precedes this item ID. Mutually exclusive with after. |
curl -X GET "https://api.rasket.com/webhooks" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"
const response = await fetch("https://api.rasket.com/webhooks", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const data = await response.json();
import os
import requests
response = requests.get(
"https://api.rasket.com/webhooks",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
print(response.json())
Response 200
{
"object": "list",
"has_more": false,
"data": [
{
"object": "webhook",
"id": "wh_3b7f21c0d9",
"endpoint": "https://acme.example.com/hooks/rasket",
"events": ["email.delivered", "email.bounced", "email.complained"],
"status": "enabled",
"signing_secret": "whsec_••••••••Ab3d",
"created_at": "2026-09-09T09:20:31.004Z"
}
]
}
One endpoint and its subscriptions.
Path parameters
Path parameters| Field | Type | Description |
|---|
webhook_id* | string | The endpoint's ID. |
curl -X GET "https://api.rasket.com/webhooks/wh_3b7f21c0d9" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"
const response = await fetch("https://api.rasket.com/webhooks/wh_3b7f21c0d9", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const { id } = await response.json();
import os
import requests
response = requests.get(
"https://api.rasket.com/webhooks/wh_3b7f21c0d9",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
id = response.json()["id"]
Response 200
{
"object": "webhook",
"id": "wh_3b7f21c0d9",
"endpoint": "https://acme.example.com/hooks/rasket",
"events": ["email.delivered", "email.bounced", "email.complained"],
"status": "enabled",
"signing_secret": "whsec_••••••••Ab3d",
"created_at": "2026-09-09T09:20:31.004Z"
}
signing_secret comes back masked. Revealing it in full is a dashboard action and is audited.
Change the URL, the subscriptions, or turn it off.
Path parameters
Path parameters| Field | Type | Description |
|---|
webhook_id* | string | The endpoint's ID. |
Body
Body| Field | Type | Description |
|---|
endpoint | string | A new absolute https URL. |
events | string[] | Replaces the subscription list. |
status | string | enabled or disabled. |
curl -X PATCH "https://api.rasket.com/webhooks/wh_3b7f21c0d9" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0" \
-H "Content-Type: application/json" \
-d '{
"events": ["email.delivered", "email.bounced", "email.complained", "email.failed"]
}'
const response = await fetch("https://api.rasket.com/webhooks/wh_3b7f21c0d9", {
method: "PATCH",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
"Content-Type": "application/json",
},
body: JSON.stringify({
events: ["email.delivered", "email.bounced", "email.complained", "email.failed"]
}),
});
const { id } = await response.json();
import os
import requests
response = requests.patch(
"https://api.rasket.com/webhooks/wh_3b7f21c0d9",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
json={
"events": ["email.delivered", "email.bounced", "email.complained", "email.failed"]
},
)
id = response.json()["id"]
Response 200
{
"object": "webhook",
"id": "wh_3b7f21c0d9"
}
- Re-enabling an endpoint resumes new events only. Older ones are replayed explicitly.
Stop delivering to this URL.
Path parameters
Path parameters| Field | Type | Description |
|---|
webhook_id* | string | The endpoint's ID. |
curl -X DELETE "https://api.rasket.com/webhooks/wh_3b7f21c0d9" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"
const response = await fetch("https://api.rasket.com/webhooks/wh_3b7f21c0d9", {
method: "DELETE",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const { id } = await response.json();
import os
import requests
response = requests.delete(
"https://api.rasket.com/webhooks/wh_3b7f21c0d9",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
id = response.json()["id"]
Response 200
{
"object": "webhook",
"id": "wh_3b7f21c0d9",
"deleted": true
}
What we tried to deliver to this endpoint.
Path parameters
Path parameters| Field | Type | Description |
|---|
webhook_id* | string | The endpoint's ID. |
Query parameters
Query parameters| Field | Type | Description |
|---|
limit | integer | How many items to return, 1–100. Defaults to 20. |
after | string | Return the page that follows this item ID. Mutually exclusive with before. |
before | string | Return the page that precedes this item ID. Mutually exclusive with after. |
›4 more fields (status, type, start_date, end_date)
Query parameters, less common| Field | Type | Description |
|---|
status | string | Only events in this state: pending, attempting, success or failed. |
type | string | Only events of this type, such as email.bounced. |
start_date | string | ISO 8601 instant, inclusive. A calendar date alone is 422 invalid_parameter — send 2026-09-09T00:00:00.000Z. |
end_date | string | ISO 8601 instant, inclusive. Must be at or after start_date, or the request is 422 invalid_parameter. |
curl -X GET "https://api.rasket.com/webhooks/wh_3b7f21c0d9/events" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"
const response = await fetch("https://api.rasket.com/webhooks/wh_3b7f21c0d9/events", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const data = await response.json();
import os
import requests
response = requests.get(
"https://api.rasket.com/webhooks/wh_3b7f21c0d9/events",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
print(response.json())
Response 200
{
"object": "list",
"has_more": true,
"data": [
{
"object": "webhook_event",
"id": "msg_2Yk1QpZ8s3XvL0nR",
"type": "email.delivered",
"status": "success",
"attempt_count": 1,
"last_http_status": 200,
"created_at": "2026-09-09T10:14:09.331Z"
}
]
}
- Event IDs start with
msg_ and are the value of the svix-id header we sent.
One event, with the exact payload we signed.
Path parameters
Path parameters| Field | Type | Description |
|---|
webhook_id* | string | The endpoint's ID. |
event_id* | string | The event's msg_ ID. |
curl -X GET "https://api.rasket.com/webhooks/wh_3b7f21c0d9/events/msg_2Yk1QpZ8s3XvL0nR" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"
const response = await fetch("https://api.rasket.com/webhooks/wh_3b7f21c0d9/events/msg_2Yk1QpZ8s3XvL0nR", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const { id } = await response.json();
import os
import requests
response = requests.get(
"https://api.rasket.com/webhooks/wh_3b7f21c0d9/events/msg_2Yk1QpZ8s3XvL0nR",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
id = response.json()["id"]
Response 200
{
"object": "webhook_event",
"id": "msg_2Yk1QpZ8s3XvL0nR",
"type": "email.delivered",
"status": "success",
"attempt_count": 1,
"last_http_status": 200,
"next_attempt_at": null,
"created_at": "2026-09-09T10:14:09.331Z",
"payload": {
"type": "email.delivered",
"created_at": "2026-09-09T10:14:09.331Z",
"data": {
"email_id": "4ef9a417-02e9-4d39-ad75-9611e0fcc33c"
}
}
}
Every delivery we made for one event, and what came back.
Path parameters
Path parameters| Field | Type | Description |
|---|
webhook_id* | string | The endpoint's ID. |
event_id* | string | The event's msg_ ID. |
Query parameters
Query parameters| Field | Type | Description |
|---|
limit | integer | How many items to return, 1–100. Defaults to 20. |
after | string | Return the page that follows this item ID. Mutually exclusive with before. |
before | string | Return the page that precedes this item ID. Mutually exclusive with after. |
curl -X GET "https://api.rasket.com/webhooks/wh_3b7f21c0d9/events/msg_2Yk1QpZ8s3XvL0nR/attempts" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"
const response = await fetch("https://api.rasket.com/webhooks/wh_3b7f21c0d9/events/msg_2Yk1QpZ8s3XvL0nR/attempts", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const data = await response.json();
import os
import requests
response = requests.get(
"https://api.rasket.com/webhooks/wh_3b7f21c0d9/events/msg_2Yk1QpZ8s3XvL0nR/attempts",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
print(response.json())
Response 200
{
"object": "list",
"has_more": false,
"data": [
{
"object": "webhook_attempt",
"id": "atmpt_6d0f9c2b71",
"attempt_number": 1,
"http_status_code": 200,
"response": "ok",
"duration_ms": 142,
"sent_at": "2026-09-09T10:14:09.480Z"
}
]
}
- Your response body is stored, truncated to 8 KB. Do not answer with anything secret.
Send the same bytes again, after you have fixed your handler.
Path parameters
Path parameters| Field | Type | Description |
|---|
webhook_id* | string | The endpoint's ID. |
event_id* | string | The event's msg_ ID. |
curl -X POST "https://api.rasket.com/webhooks/wh_3b7f21c0d9/events/msg_2Yk1QpZ8s3XvL0nR/replay" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"
const response = await fetch("https://api.rasket.com/webhooks/wh_3b7f21c0d9/events/msg_2Yk1QpZ8s3XvL0nR/replay", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const { id } = await response.json();
import os
import requests
response = requests.post(
"https://api.rasket.com/webhooks/wh_3b7f21c0d9/events/msg_2Yk1QpZ8s3XvL0nR/replay",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
id = response.json()["id"]
Response 200
{
"object": "webhook_event",
"id": "msg_2Yk1QpZ8s3XvL0nR"
}
- A replay re-sends the identical payload with the same
svix-id and a fresh timestamp and signature — so a handler that dedupes on svix-id will correctly ignore it. - Ten replays per minute per team. Replaying to a disabled endpoint is refused.
Issue a new secret with a 24-hour overlap.
Path parameters
Path parameters| Field | Type | Description |
|---|
webhook_id* | string | The endpoint's ID. |
curl -X POST "https://api.rasket.com/webhooks/wh_3b7f21c0d9/rotate-secret" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"
const response = await fetch("https://api.rasket.com/webhooks/wh_3b7f21c0d9/rotate-secret", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const { id } = await response.json();
import os
import requests
response = requests.post(
"https://api.rasket.com/webhooks/wh_3b7f21c0d9/rotate-secret",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
id = response.json()["id"]
Response 200
{
"object": "webhook",
"id": "wh_3b7f21c0d9",
"signing_secret": "whsec_8pQ2vLmT0yZ4cRn7bKw1sXf6"
}
- For 24 hours both secrets sign, and
svix-signature carries both signatures space-separated. Accept any one that verifies and the rotation needs no downtime. - The new secret is shown once, exactly like the first one.
The events created while the endpoint was disabled, oldest first.
Path parameters
Path parameters| Field | Type | Description |
|---|
webhook_id* | string | The endpoint's ID. |
Query parameters
Query parameters| Field | Type | Description |
|---|
limit | integer | How many to return, 1–100. has_more says whether more are parked. |
curl -X GET "https://api.rasket.com/webhooks/wh_3b7f21c0d9/parked?limit=20" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"
const response = await fetch("https://api.rasket.com/webhooks/wh_3b7f21c0d9/parked?limit=20", {
method: "GET",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const data = await response.json();
import os
import requests
response = requests.get(
"https://api.rasket.com/webhooks/wh_3b7f21c0d9/parked?limit=20",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
print(response.json())
Response 200
{
"object": "list",
"has_more": false,
"data": [
{
"id": "msg_2Yk1QpZ8s3XvL0nR",
"type": "email.delivered",
"created_at": "2026-09-09T09:20:33.412Z",
"status": "failed",
"attempt_count": 6,
"last_http_status": 503
}
]
}
- A disabled endpoint keeps receiving events without delivering them. They are listed oldest first, the order they are delivered in.
Replay the oldest parked events, in order, within the replay budget.
Path parameters
Path parameters| Field | Type | Description |
|---|
webhook_id* | string | The endpoint's ID. |
curl -X POST "https://api.rasket.com/webhooks/wh_3b7f21c0d9/parked/deliver" \
-H "Authorization: Bearer $RASKET_API_KEY" \
-H "User-Agent: acme-billing/1.0"
const response = await fetch("https://api.rasket.com/webhooks/wh_3b7f21c0d9/parked/deliver", {
method: "POST",
headers: {
Authorization: `Bearer ${process.env.RASKET_API_KEY}`,
"User-Agent": "acme-billing/1.0",
},
});
const data = await response.json();
import os
import requests
response = requests.post(
"https://api.rasket.com/webhooks/wh_3b7f21c0d9/parked/deliver",
headers={
"Authorization": f"Bearer {os.environ['RASKET_API_KEY']}",
"User-Agent": "acme-billing/1.0",
},
)
print(response.json())
Response 200
{
"object": "parked_delivery",
"delivered": 10,
"remaining": 32,
"stopped_reason": "rate_limit_exceeded"
}
- At most 10 events per call, inside the same budget as a single replay: 10 a minute per team. When it runs out, delivery stops with
stopped_reason: "rate_limit_exceeded". - Call again while
remaining is above zero. A disabled endpoint is 422 validation_error: enable it first.