Seegnals

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

  • 201 when the subscription is new.
  • 200 with 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 Content on 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": "…" }.