REST API
Subscriptions (REST Hooks)
Subscribe a URL to one event type and unsubscribe it again. Built for Zapier-style REST Hooks and for wiring your own receiver without the Webhooks tab.
Updated 4 September 2026
Subscriptions are the API twin of the workspace webhook. Where the Webhooks tab lets you save one URL that receives every event type, a subscription binds one URL to one event key. You can have many. Both are delivered by the same worker, signed with the same secret, and appear in the same delivery log.
POST /api/v1/hooks
curl -X POST https://app.seegnals.com/api/v1/hooks \
-H "Authorization: Bearer $SEEGNALS_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "event": "positive_reply", "target_url": "https://hooks.example.com/seegnals/positive" }'
const res = await fetch("https://app.seegnals.com/api/v1/hooks", {
method: "POST",
headers: {
"Authorization": `Bearer ${process.env.SEEGNALS_TOKEN}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"event": "positive_reply",
"target_url": "https://hooks.example.com/seegnals/positive"
})
});
const json = await res.json().catch(() => null);
console.log(res.status, json);
import os, requests
res = requests.post(
"https://app.seegnals.com/api/v1/hooks",
headers={"Authorization": f"Bearer {os.environ['SEEGNALS_TOKEN']}"}, json={
"event": "positive_reply",
"target_url": "https://hooks.example.com/seegnals/positive"
},
timeout=20,
)
print(res.status_code, res.json() if res.content else None)
Request body
| Field | Type | Required | Notes |
|---|---|---|---|
event |
string | yes | One of new_reply, positive_reply, bounce, unsubscribe. |
target_url |
string | yes | Must be https://, public, without credentials. Checked by the same URL guard as the webhook URL, including a DNS lookup. Stored normalised. |
Body limit 4 KiB.
Event keys
| Key | Fires when |
|---|---|
new_reply |
A prospect replied. Fires before the classification is known. |
positive_reply |
The reply classifier marked a reply as interested. Always arrives after new_reply for the same reply. |
bounce |
A message to this prospect bounced. |
unsubscribe |
The prospect used the one-click unsubscribe link. |
There is no subscription key for sent or clicked; use the workspace webhook for those.
Response
201when the subscription is new.200with the existing row when the same(event, target_url)already exists in your workspace. The call is idempotent.
{ "id": "3f2b1c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", "event": "positive_reply", "target_url": "https://hooks.example.com/seegnals/positive" }
Creating a subscription also makes sure your workspace has a signing secret. If you have never saved a URL in the Webhooks tab, the secret exists but the Reveal signing secret button only appears once a webhook URL is saved. Save one (it can be the same URL) to read the secret, or wait for the fix noted in the changelog.
Errors
| Status | Body |
|---|---|
400 |
{ "error": "`event` must be one of: new_reply, positive_reply, bounce, unsubscribe." } |
400 |
{ "error": "`target_url` must be a non-empty string." } |
400 |
{ "error": "<URL guard reason>" }, for example The webhook URL must start with https:// or hooks.internal is a local address — the webhook url must point at a public host. |
500 |
{ "error": "Couldn't create the subscription: …" } |
GET /api/v1/hooks
Query: limit and cursor.
{ "data": [ { "id": "…", "event": "new_reply", "target_url": "https://hooks.zapier.com/hooks/standard/…", "created_at": "2026-09-01T00:00:00.000+00:00" } ], "next_cursor": null }
Subscriptions only. The workspace webhook URL from the Webhooks tab and the signing secret are never returned by the API.
DELETE /api/v1/hooks/{id}
curl -X DELETE https://app.seegnals.com/api/v1/hooks/3f2b1c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d \
-H "Authorization: Bearer $SEEGNALS_TOKEN"
const res = await fetch("https://app.seegnals.com/api/v1/hooks/3f2b1c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d", {
method: "DELETE",
headers: {
"Authorization": `Bearer ${process.env.SEEGNALS_TOKEN}`
}
});
const json = await res.json().catch(() => null);
console.log(res.status, json);
import os, requests
res = requests.delete(
"https://app.seegnals.com/api/v1/hooks/3f2b1c4d-5e6f-4a7b-8c9d-0e1f2a3b4c5d",
headers={"Authorization": f"Bearer {os.environ['SEEGNALS_TOKEN']}"},
timeout=20,
)
print(res.status_code, res.json() if res.content else None)
204 No Contenton success.404 { "error": "No subscription with this id." }for an unknown id, another workspace’s id, or a value that is not a UUID.
Deliveries already queued for that URL are not cancelled; they finish their attempts.
What a subscription receives
The same request as the workspace webhook, described in Webhooks, with two differences: X-Seegnals-Event and the payload type carry the subscription key (new_reply, not replied), and for positive_reply the payload id is the reply id with metadata: { "replyId": "…" }.