> For the complete documentation index see https://domscan.net/llms.txt. Markdown versions of every docs page are available by appending .md to the page URL.

Source: https://domscan.net/de/docs/watchlist

# Domain-Beobachtungsliste

DomScan-API: Überwachen Sie Domänen auf Ablauf und Verfügbarkeitsänderungen. Erhalten Sie Benachrichtigungen, wenn registrierte Domänen verfügbar werden.

Überwachen Sie Domänen auf Ablauf und Verfügbarkeitsänderungen. Erhalten Sie Benachrichtigungen, wenn registrierte Domänen verfügbar werden.

**Überwachung**

Melden Sie sich mit Google an, um auf Ihre personalisierte Domain-Watchlist mit E-Mail-Benachrichtigungen, Webhooks und mehr zuzugreifen.

Erhalten Sie Benachrichtigungen per E-Mail, Telegram, Slack, Discord oder Webhook.

[Überwachung](https://domscan.net/de/monitor)
[Einstellungen](https://domscan.net/de/monitor/settings)
[Markenüberwachung](https://domscan.net/de/brand-monitor-api)

GET /v1/watchlist

Rufen Sie alle Domänen in Ihrer Beobachtungsliste ab.

#### Beispielantwort

```json
{
                "watchlist": [
                {
                "domain": "startup.com",
                "added_at": "2024-01-15T10:30:00Z",
                "status": "registered",
                "expiry_date": "2025-01-15T00:00:00Z",
                "days_until_expiry": 287,
                "last_checked": "2024-03-01T12:00:00Z",
                "notify_email": "alerts@example.com",
                "check_count": 4
                }
                ],
                "total": 1,
                "updated_at": "2024-03-01T12:00:00Z"
                }
```

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `has_more` | boolean |
| `next_cursor` | string \| null |
| `watchlist[]` | object[] |
| `watchlist[]` | object |
| `watchlist[].domain` | string |
| `watchlist[].added_at` | string |
| `watchlist[].status` | string |
| `watchlist[].last_checked` | string |
| `watchlist[].expiry_date` | string |
| `watchlist[].days_until_expiry` | integer |
| `watchlist[].notify_email` | string |
| `watchlist[].check_count` | integer |
| `total` | integer |
| `updated_at` | string |

POST /v1/watchlist

Fügen Sie eine Domäne zu Ihrer Beobachtungsliste hinzu.

#### Anfragetext

```json
{
                "domain": "mydomain.com",
                "notify_email": "alerts@example.com"
                }
```

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `success` | boolean |
| `message` | string |
| `watchlist[]` | object[] |
| `watchlist[]` | object |
| `watchlist[].domain` | string |
| `watchlist[].added_at` | string |
| `watchlist[].status` | string |
| `watchlist[].last_checked` | string |
| `watchlist[].expiry_date` | string |
| `watchlist[].days_until_expiry` | integer |
| `watchlist[].notify_email` | string |
| `watchlist[].check_count` | integer |
| `total` | integer |

#### Beispielantwort

```json
{
  "success": true,
  "message": "Added example.org to watchlist",
  "watchlist": [
    {
      "domain": "example.com",
      "added_at": "2026-06-02T09:12:00.000Z",
      "last_checked": "2026-10-09T03:00:12.000Z",
      "status": "expiring_soon",
      "expiry_date": "2026-12-18T14:22:08Z",
      "days_until_expiry": 70,
      "notify_email": "alerts@example.com"
    },
    {
      "domain": "example.org",
      "added_at": "2026-10-09T10:15:30.000Z",
      "status": "unknown",
      "notify_email": "alerts@example.com"
    }
  ],
  "total": 2
}
```

DELETE /v1/watchlist?domain=mydomain.com

Von Watchlist entfernen

```bash
curl -X DELETE -H "X-API-Key: your-api-key" "https://domscan.net/v1/watchlist?domain=mydomain.com"
```

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `success` | boolean |
| `message` | string |
| `domain` | string |
| `watchlist[]` | object[] |
| `watchlist[]` | object |
| `watchlist[].domain` | string |
| `watchlist[].added_at` | string |
| `watchlist[].status` | string |
| `watchlist[].last_checked` | string |
| `watchlist[].expiry_date` | string |
| `watchlist[].days_until_expiry` | integer |
| `watchlist[].notify_email` | string |
| `watchlist[].check_count` | integer |
| `total` | integer |

#### Beispielantwort

```json
{
  "success": true,
  "message": "Request completed",
  "domain": "example.com",
  "watchlist": [
    {
      "domain": "example.com",
      "added_at": "2026-08-27T12:00:00Z",
      "status": "registered",
      "last_checked": "2026-08-27T12:00:00Z",
      "expiry_date": "2026-08-27T12:00:00Z",
      "days_until_expiry": 1,
      "notify_email": "user@example.com",
      "check_count": 1
    }
  ],
  "total": 1
}
```

GET /v1/watchlist/expiring?days=30

Läuft bald ab

#### Beispielantwort

```json
{
                "expiring_domains": [
                {
                "domain": "renewal-window.com",
                "status": "expiring_soon",
                "expiry_date": "2026-05-12T00:00:00Z",
                "days_until_expiry": 28,
                "check_count": 11
                }
                ],
                "total": 1,
                "threshold_days": 30
                }
```

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `has_more` | boolean |
| `next_cursor` | string \| null |
| `expiring_domains[]` | object[] |
| `expiring_domains[]` | object |
| `expiring_domains[].domain` | string |
| `expiring_domains[].added_at` | string |
| `expiring_domains[].status` | string |
| `expiring_domains[].last_checked` | string |
| `expiring_domains[].expiry_date` | string |
| `expiring_domains[].days_until_expiry` | integer |
| `expiring_domains[].notify_email` | string |
| `expiring_domains[].check_count` | integer |
| `total` | integer |
| `threshold_days` | integer |

GET /v1/watchlist/available

Verfügbar

Paginiert: Sende `limit` (1 bis 100) und gib `next_cursor` als `cursor` zurück. Antworten enthalten `has_more` und `next_cursor`. Ohne `limit` wird die gesamte Liste zurückgegeben. Siehe [Pagination](https://domscan.net/docs/response-format#pagination).

#### Beispielantwort

```json
{
                "available_domains": [
                {
                "domain": "startupdrop.ai",
                "status": "available",
                "last_checked": "2026-04-14T08:30:00Z",
                "check_count": 18
                }
                ],
                "total": 1,
                "message": "Diese Domains aus Ihrer Beobachtungsliste sind jetzt zur Registrierung verfügbar!"
                }
```

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `has_more` | boolean |
| `next_cursor` | string \| null |
| `available_domains[]` | object[] |
| `available_domains[]` | object |
| `available_domains[].domain` | string |
| `available_domains[].added_at` | string |
| `available_domains[].status` | string |
| `available_domains[].last_checked` | string |
| `available_domains[].expiry_date` | string |
| `available_domains[].days_until_expiry` | integer |
| `available_domains[].notify_email` | string |
| `available_domains[].check_count` | integer |
| `total` | integer |
| `message` | string |

### Alarm-Webhooks

Empfangen Sie Überwachungsalarme als signierte JSON-POST-Anfragen an Ihren eigenen HTTPS-Endpunkten. Verwalten Sie Endpunkte mit einem API-Schlüssel oder, wenn Sie angemeldet sind, in den Monitor-Einstellungen.

#### Ereignistypen

| Monitor | Ereignisse |
| --- | --- |
| Verfügbarkeit, Ablauf und Status der Watchlist | `domain.available`, `domain.expiring_30`, `domain.expiring_14`, `domain.expiring_7`, `domain.expiring_1`, `domain.status_change` |
| Überwachung von Domain-Änderungen | `domain.dns_changed`, `domain.certificate_changed`, `domain.registration_changed`, `domain.hosting_changed`, `domain.technology_changed`, `domain.email_security_changed` |
| Markenüberwachung (neue ähnliche Registrierungen) | `brand.threat_detected` |
| Alarm-Abonnements der Beobachtungsliste (Zertifikate, Preise, DNS-Einträge, ähnliche Domains) | `ssl.expiring`, `ssl.expired`, `ssl.changed`, `price.dropped`, `dns.changed`, `typosquat.detected` |
| Asynchrone Jobs (kostenlos, nur für Jobs, die mit notify_webhook erstellt wurden) | `job.completed`, `job.failed`, `job.cancelled` |
| Testereignis (kostenlos) | `webhook.test` |

[Leitfaden zur Webhook-Alarm-API lesen](https://domscan.net/de/webhook-alerts-api)

#### Preise

Jeder mit einer 2xx-Antwort an einen Endpunkt zugestellte Alarm kostet 2 Credits. Die Credits werden unmittelbar vor dem ersten Versuch reserviert und erstattet, wenn die Zustellung fehlschlägt, abgebrochen oder der Endpunkt gelöscht wird. Testereignisse sind kostenlos. Reicht das Guthaben nicht aus, wird der Alarm übersprungen und nichts berechnet.

POST /v1/webhooks

Registriert einen HTTPS-Endpunkt. Die URL muss auf eine öffentliche Adresse auflösen. Lassen Sie events weg oder übergeben Sie ["*"], um jeden Ereignistyp zu empfangen. Das Signaturgeheimnis wird nur in dieser Antwort zurückgegeben.

#### Beispielanfrage

```bash
curl -X POST "https://domscan.net/v1/webhooks" \
  -H "X-API-Key: $DOMSCAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://hooks.example.com/domscan",
    "events": ["domain.expiring_30", "domain.available", "domain.dns_changed"]
  }'
```

#### Beispielantwort

```json
{
  "endpoint": {
    "id": "whe_3f1c0a9b8e7d6c5b4a39281706f5e4d3",
    "url": "https://hooks.example.com/domscan",
    "events": ["domain.available", "domain.expiring_30", "domain.dns_changed"],
    "status": "active",
    "secret_hint": "9c2e"
  },
  "secret": "whsec_..."
}
```

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `endpoint` | object |
| `endpoint.id` | string |
| `endpoint.url` | string |
| `endpoint.description` | string \| null |
| `endpoint.events[]` | string[] |
| `endpoint.status` | string |
| `endpoint.disabled_reason` | string \| null |
| `endpoint.consecutive_failures` | integer |
| `endpoint.secret_hint` | string |
| `endpoint.previous_secret_expires_at` | string \| null |
| `endpoint.secret_rotated_at` | string \| null |
| `endpoint.last_success_at` | string \| null |
| `endpoint.last_failure_at` | string \| null |
| `endpoint.last_error` | string \| null |
| `endpoint.created_at` | string |
| `endpoint.updated_at` | string |
| `endpoint.disabled_at` | string \| null |
| `secret` | string |

GET /v1/webhooks

Listet Ihre Endpunkte mit Status, abonnierten Ereignissen, Fehleranzahl und den letzten vier Zeichen des Geheimnisses auf, zusammen mit den verfügbaren Ereignistypen und der Zustellungsrichtlinie.

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `endpoints[]` | object[] |
| `endpoints[]` | object |
| `endpoints[].id` | string |
| `endpoints[].url` | string |
| `endpoints[].description` | string \| null |
| `endpoints[].events[]` | string[] |
| `endpoints[].status` | string |
| `endpoints[].disabled_reason` | string \| null |
| `endpoints[].consecutive_failures` | integer |
| `endpoints[].secret_hint` | string |
| `endpoints[].previous_secret_expires_at` | string \| null |
| `endpoints[].secret_rotated_at` | string \| null |
| `endpoints[].last_success_at` | string \| null |
| `endpoints[].last_failure_at` | string \| null |
| `endpoints[].last_error` | string \| null |
| `endpoints[].created_at` | string |
| `endpoints[].updated_at` | string |
| `endpoints[].disabled_at` | string \| null |
| `available_events[]` | string[] |
| `all_events` | string |
| `test_event` | string |
| `policy` | object |
| `policy.secret_rotation_grace_seconds` | integer |
| `policy.secret_rotation_credits` | integer |
| `policy.credits_per_delivered_alert` | integer |
| `policy.test_event_credits` | integer |
| `policy.job_event_credits` | integer |
| `policy.failed_deliveries_charged` | boolean |
| `policy.max_endpoints` | integer |
| `policy.max_attempts` | integer |
| `policy.disable_after_consecutive_failures` | integer |
| `policy.signature_tolerance_seconds` | integer |

#### Beispielantwort

```json
{
  "endpoints": [
    {
      "id": "example-id",
      "url": "https://example.com",
      "events": [
        "example"
      ],
      "status": "active",
      "secret_hint": "example",
      "created_at": "2026-08-27T12:00:00Z",
      "description": "example",
      "disabled_reason": "customer",
      "consecutive_failures": 1,
      "previous_secret_expires_at": "2026-08-27T12:00:00Z",
      "secret_rotated_at": "2026-08-27T12:00:00Z",
      "last_success_at": "2026-08-27T12:00:00Z",
      "last_failure_at": "2026-08-27T12:00:00Z",
      "last_error": "example",
      "updated_at": "2026-08-27T12:00:00Z",
      "disabled_at": "2026-08-27T12:00:00Z"
    }
  ],
  "available_events": [
    "domain.available"
  ],
  "policy": {
    "credits_per_delivered_alert": 2,
    "test_event_credits": 0,
    "failed_deliveries_charged": false,
    "max_endpoints": 1,
    "max_attempts": 1,
    "disable_after_consecutive_failures": 1,
    "signature_tolerance_seconds": 1,
    "secret_rotation_grace_seconds": 86400,
    "secret_rotation_credits": 0,
    "job_event_credits": 0
  },
  "all_events": "*",
  "test_event": "webhook.test"
}
```

PATCH /v1/webhooks/:webhook_id

Pausiert oder aktiviert einen Endpunkt, ändert seine Ereignisse oder bearbeitet seine Beschreibung. Beim Aktivieren eines Endpunkts wird seine Fehleranzahl zurückgesetzt.

```json
{ "enabled": true, "events": ["*"] }
```

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `endpoint` | object |
| `endpoint.id` | string |
| `endpoint.url` | string |
| `endpoint.description` | string \| null |
| `endpoint.events[]` | string[] |
| `endpoint.status` | string |
| `endpoint.disabled_reason` | string \| null |
| `endpoint.consecutive_failures` | integer |
| `endpoint.secret_hint` | string |
| `endpoint.previous_secret_expires_at` | string \| null |
| `endpoint.secret_rotated_at` | string \| null |
| `endpoint.last_success_at` | string \| null |
| `endpoint.last_failure_at` | string \| null |
| `endpoint.last_error` | string \| null |
| `endpoint.created_at` | string |
| `endpoint.updated_at` | string |
| `endpoint.disabled_at` | string \| null |

#### Beispielantwort

```json
{
  "endpoint": {
    "id": "example-id",
    "url": "https://example.com",
    "events": [
      "example"
    ],
    "status": "active",
    "secret_hint": "example",
    "created_at": "2026-08-27T12:00:00Z",
    "description": "example",
    "disabled_reason": "customer",
    "consecutive_failures": 1,
    "previous_secret_expires_at": "2026-08-27T12:00:00Z",
    "secret_rotated_at": "2026-08-27T12:00:00Z",
    "last_success_at": "2026-08-27T12:00:00Z",
    "last_failure_at": "2026-08-27T12:00:00Z",
    "last_error": "example",
    "updated_at": "2026-08-27T12:00:00Z",
    "disabled_at": "2026-08-27T12:00:00Z"
  }
}
```

DELETE /v1/webhooks/:webhook_id

Löscht einen Endpunkt. Ausstehende Zustellungen werden abgebrochen und reservierte Credits erstattet.

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `id` | string |
| `deleted` | boolean |

#### Beispielantwort

```json
{
  "id": "example-id",
  "deleted": true
}
```

POST /v1/webhooks/:webhook_id/test

Sendet ein kostenloses Ereignis webhook.test und gibt das Ergebnis zurück. Testereignisse werden nicht wiederholt und zählen nicht für die automatische Pausierung.

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `delivery` | object |
| `delivery.id` | string |
| `delivery.endpoint_id` | string |
| `delivery.event_id` | string |
| `delivery.event_type` | string |
| `delivery.status` | string |
| `delivery.attempts` | integer |
| `delivery.next_attempt_at` | string \| null |
| `delivery.last_attempt_at` | string \| null |
| `delivery.response_status` | integer \| null |
| `delivery.last_error` | string \| null |
| `delivery.credits_charged` | integer |
| `delivery.refunded` | boolean |
| `delivery.created_at` | string |
| `delivery.delivered_at` | string \| null |
| `delivered` | boolean |

#### Beispielantwort

```json
{
  "delivery": {
    "id": "example-id",
    "endpoint_id": "example",
    "event_id": "example",
    "event_type": "domain",
    "status": "pending",
    "attempts": 1,
    "next_attempt_at": "2026-08-27T12:00:00Z",
    "last_attempt_at": "2026-08-27T12:00:00Z",
    "response_status": 1,
    "last_error": "example",
    "credits_charged": 1,
    "refunded": false,
    "created_at": "2026-08-27T12:00:00Z",
    "delivered_at": "2026-08-27T12:00:00Z"
  },
  "delivered": false
}
```

POST /v1/webhooks/:webhook_id/rotate-secret

Ersetzt das Signaturgeheimnis eines Endpunkts, auch eines pausierten. Das neue Geheimnis wird nur in dieser Antwort zurückgegeben. In den nächsten 24 Stunden wird jede Zustellung mit dem neuen und dem vorherigen Geheimnis signiert. Die Rotation ist kostenlos.

#### Beispielanfrage

```bash
curl -X POST "https://domscan.net/v1/webhooks/whe_3f1c0a9b8e7d6c5b4a39281706f5e4d3/rotate-secret" \
  -H "X-API-Key: $DOMSCAN_API_KEY"
```

#### Beispielantwort

```json
{
  "endpoint": {
    "id": "whe_3f1c0a9b8e7d6c5b4a39281706f5e4d3",
    "status": "active",
    "secret_hint": "4b7a",
    "previous_secret_expires_at": "2026-10-11T12:00:00.000Z"
  },
  "secret": "whsec_...",
  "previous_secret_expires_at": "2026-10-11T12:00:00.000Z",
  "grace_period_seconds": 86400
}
```

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `endpoint` | object |
| `endpoint.id` | string |
| `endpoint.url` | string |
| `endpoint.description` | string \| null |
| `endpoint.events[]` | string[] |
| `endpoint.status` | string |
| `endpoint.disabled_reason` | string \| null |
| `endpoint.consecutive_failures` | integer |
| `endpoint.secret_hint` | string |
| `endpoint.previous_secret_expires_at` | string \| null |
| `endpoint.secret_rotated_at` | string \| null |
| `endpoint.last_success_at` | string \| null |
| `endpoint.last_failure_at` | string \| null |
| `endpoint.last_error` | string \| null |
| `endpoint.created_at` | string |
| `endpoint.updated_at` | string |
| `endpoint.disabled_at` | string \| null |
| `secret` | string |
| `previous_secret_expires_at` | string |
| `grace_period_seconds` | integer |

GET /v1/webhooks/:webhook_id/deliveries

#### Abfrageparameter

| Parameter | Typ | Beschreibung |
| --- | --- | --- |
| limit | integer | Anzahl der zurückzugebenden Zustellungen, von 1 bis 100. Standard: 50. |

Listet aktuelle Zustellungen mit Status, Versuchen, HTTP-Antwort, berechneten Credits und Erstattungen auf. Zustellungsprotokolle werden 30 Tage lang aufbewahrt.

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `endpoint_id` | string |
| `deliveries[]` | object[] |
| `deliveries[]` | object |
| `deliveries[].id` | string |
| `deliveries[].endpoint_id` | string |
| `deliveries[].event_id` | string |
| `deliveries[].event_type` | string |
| `deliveries[].status` | string |
| `deliveries[].attempts` | integer |
| `deliveries[].next_attempt_at` | string \| null |
| `deliveries[].last_attempt_at` | string \| null |
| `deliveries[].response_status` | integer \| null |
| `deliveries[].last_error` | string \| null |
| `deliveries[].credits_charged` | integer |
| `deliveries[].refunded` | boolean |
| `deliveries[].created_at` | string |
| `deliveries[].delivered_at` | string \| null |

#### Beispielantwort

```json
{
  "endpoint_id": "example",
  "deliveries": [
    {
      "id": "example-id",
      "endpoint_id": "example",
      "event_id": "example",
      "event_type": "domain",
      "status": "pending",
      "attempts": 1,
      "next_attempt_at": "2026-08-27T12:00:00Z",
      "last_attempt_at": "2026-08-27T12:00:00Z",
      "response_status": 1,
      "last_error": "example",
      "credits_charged": 1,
      "refunded": false,
      "created_at": "2026-08-27T12:00:00Z",
      "delivered_at": "2026-08-27T12:00:00Z"
    }
  ]
}
```

#### Anfrageformat

Jeder Alarm ist eine POST-Anfrage mit JSON-Body. Das Feld id bleibt für ein Ereignis gleich und wird in X-DomScan-Event-Id wiederholt, sodass Sie bereits verarbeitete Wiederholungen ignorieren können.

```http
POST /domscan HTTP/1.1
Content-Type: application/json
X-DomScan-Event: domain.expiring_30
X-DomScan-Event-Id: evt_6f1d2c3b4a5948e7b6c5d4e3f2a1b0c9
X-DomScan-Timestamp: 1791633600
X-DomScan-Signature: sha256=...

{
  "id": "evt_6f1d2c3b4a5948e7b6c5d4e3f2a1b0c9",
  "type": "domain.expiring_30",
  "created": "2026-10-10T12:00:00.000Z",
  "data": {
    "domain": "acme.io",
    "previous_status": "registered",
    "current_status": "expiring_soon",
    "expiry_date": "2026-11-09T00:00:00Z",
    "days_until_expiry": 30,
    "checked_at": "2026-10-10T12:00:00.000Z"
  }
}
```

#### Signaturen prüfen

Berechnen Sie mit dem Geheimnis Ihres Endpunkts einen HMAC-SHA256 aus dem Wert von X-DomScan-Timestamp, einem Punkt und dem unveränderten Anfrage-Body. Vergleichen Sie das Ergebnis in konstanter Zeit mit X-DomScan-Signature und weisen Sie Anfragen zurück, deren Zeitstempel älter als 5 Minuten ist.

Das folgende Beispiel prüft jede Signatur im Header und funktioniert daher auch während einer Rotation des Geheimnisses weiter.

```javascript
import crypto from 'node:crypto';

// rawBody: the request body exactly as received, before JSON parsing.
function verifyDomScanWebhook(rawBody, headers, secret) {
  const timestamp = headers['x-domscan-timestamp'];
  const header = headers['x-domscan-signature'] || '';
  if (!/^\d{10}$/.test(timestamp || '')) return false;
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) return false;
  const expected = Buffer.from('sha256=' + crypto
    .createHmac('sha256', secret)
    .update(timestamp + '.' + rawBody)
    .digest('hex'));
  // One entry normally; two during a secret rotation (new secret first).
  return header.split(',').some((entry) => {
    const candidate = Buffer.from(entry.trim());
    return candidate.length === expected.length &&
      crypto.timingSafeEqual(candidate, expected);
  });
}
```

#### Signaturgeheimnis rotieren

Während der 24-stündigen Übergangsfrist nach einer Rotation enthält X-DomScan-Signature zwei durch ein Komma getrennte Signaturen, das neue Geheimnis zuerst, wie unten gezeigt. Akzeptieren Sie eine Anfrage, wenn ein Eintrag zu einem Geheimnis passt, dem Sie vertrauen, setzen Sie das neue Geheimnis ein und vertrauen Sie dann dem alten nicht mehr. Wenn Sie während der Übergangsfrist erneut rotieren, wird das ältere vorherige Geheimnis sofort außer Kraft gesetzt.

```http
X-DomScan-Signature: sha256=<new>,sha256=<previous>
```

#### Wiederholungen und automatische Pausierung

Geben Sie innerhalb von 8 Sekunden einen beliebigen 2xx-Status zurück, um einen Alarm zu bestätigen. Weiterleitungen, andere Statuscodes und Zeitüberschreitungen werden mit exponentiellem Backoff wiederholt (1, 4, 16, 64 und 256 Minuten), insgesamt bis zu 6 Versuche. Nach 15 aufeinanderfolgenden fehlgeschlagenen Versuchen wird der Endpunkt pausiert; aktivieren Sie ihn per PATCH oder in den Monitor-Einstellungen wieder.

Wird ein Endpunkt nach wiederholten Fehlern pausiert, erhält der Inhaber des Endpunkts eine E-Mail mit dem Host des Endpunkts, dem letzten Fehler und einem Link zu den Monitor-Einstellungen. Beheben Sie das Problem am Endpunkt und aktivieren Sie ihn dann wieder in den Monitor-Einstellungen oder mit PATCH und enabled auf true. Beim Aktivieren wird der Fehlerzähler auf null zurückgesetzt.

### Alarm-Abonnements der Beobachtungsliste

Abonnieren Sie für ein beobachtetes Element die gewünschten Alarme. Jedes Abonnement beobachtet einen Hostnamen, eine TLD oder eine Markendomain, und Sie wählen, welche Ereignisse Sie erhalten. Die Ereignisse gehen an Ihre signierten Webhook-Endpunkte und an Ihre Alarme in der App.

| Typ | Ereignisse | Wann es ausgelöst wird |
| --- | --- | --- |
| `ssl` | `ssl.expiring`, `ssl.expired`, `ssl.changed` | ssl.expiring, wenn das von einem Hostnamen ausgelieferte Zertifikat innerhalb von ssl_expiring_days abläuft (30, 14, 7 oder 1, Standard 14), einmal pro Zertifikat und Schwellenwert. ssl.expired einmal pro abgelaufenem Zertifikat. ssl.changed, wenn bei zwei aufeinanderfolgenden Prüfungen ein neuer Zertifikat-Fingerprint gesehen wird; die Payload gibt an, ob sich der Aussteller geändert hat. |
| `price` | `price.dropped` | Wenn der Registrierungs- oder Verlängerungspreis einer TLD, bei einem Registrar oder beim günstigsten, seit dem letzten Alarm um mindestens drop_percent vom höchsten Preis fällt oder target_price oder weniger erreicht. Die Preise stammen aus dem täglichen Preis-Snapshot, und jeder Snapshot wird einmal ausgewertet. |
| `dns` | `dns.changed` | Wenn ein beobachteter Eintragstyp (A, AAAA, MX, NS, TXT oder CNAME) eines beliebigen Hostnamens bei zwei aufeinanderfolgenden bestätigten Abfragen dieselbe neue Antwort liefert. |
| `typosquat` | `typosquat.detected` | Wenn eine ähnliche Domain zur Markendomain neu registriert wird, bestätigt durch eine maßgebliche Registrierungsabfrage. Der erste vollständige Durchlauf über die ähnlichen Domains legt die Ausgangsbasis fest und löst keinen Alarm aus. |

Nur eindeutige Belege lösen ein Ereignis aus. Ein fehlgeschlagener TLS-Handshake, eine DNS-Abfrage, die fehlschlägt oder in ein Timeout läuft, ein fehlender Preis und eine blockierte oder ratenbegrenzte Abfrage gelten als unbekannt: Sie ändern nichts und lösen nie einen Alarm oder eine Abbuchung aus. Die erste bestätigte Prüfung eines Abonnements legt dessen Ausgangsbasis fest.

Die Prüfungen laufen automatisch: Zertifikate alle 12 Stunden, DNS-Einträge stündlich (mit einer Bestätigungsprüfung 15 Minuten nach der ersten Erkennung einer Änderung), Preise alle 2 Stunden gegen den täglichen Snapshot und ähnliche Domains in Batches zu 30 mit einem vollständigen Durchlauf pro Tag. Nach einer unbekannten Prüfung erfolgt der nächste Versuch früher.

Das Anlegen von Abonnements und das Ausführen von Prüfungen ist kostenlos. Jeder an einen Webhook-Endpunkt zugestellte Alarm mit 2xx-Antwort kostet 2 Credits, wie jeder andere Monitoring-Alarm. Fehlgeschlagene Zustellungen werden erstattet, und ein Konto kann bis zu 100 Abonnements haben, davon 20 für ähnliche Domains.

POST /v1/watchlist/alerts

Legt ein Abonnement an. type bestimmt, welche Felder gelten: domain für ssl, dns und typosquat; tld, registrar, price_type, drop_percent und target_price für price. Felder, die nicht zum Typ passen, werden abgelehnt.

#### Anfragetext

| Parameter | Typ | Beschreibung |
| --- | --- | --- |
| type | string | ssl, price, dns oder typosquat. Erforderlich. |
| domain | string | Zu beobachtender Hostname für ssl und dns oder die Markendomain für typosquat. |
| tld | string | Zu beobachtende TLD für price, zum Beispiel com. |
| registrar | string | Registrar-ID für price. Weglassen, um den günstigsten Registrar zu beobachten. |
| events | string[] | Zu empfangende Ereignisse. Standardmäßig alle Ereignisse des Typs. |
| ssl_expiring_days | integer | Tage vor Ablauf für ssl.expiring: 30, 14, 7 oder 1. Standard 14. |
| price_type | string | registration (Standard) oder renewal. |
| drop_percent | number | Alarm bei einem Rückgang um mindestens diesen Prozentsatz, 1 bis 95. |
| target_price | number | Alarm, wenn der Preis in USD diesen Betrag oder weniger erreicht. Ein Preis-Abonnement benötigt drop_percent, target_price oder beides. |
| record_types | string[] | Zu beobachtende Eintragstypen für dns. Standardmäßig A, AAAA, MX, NS, TXT und CNAME. |

#### Beispielanfrage

```bash
curl -X POST "https://domscan.net/v1/watchlist/alerts" \
  -H "X-API-Key: $DOMSCAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "price",
    "tld": "com",
    "registrar": "porkbun",
    "price_type": "renewal",
    "drop_percent": 10,
    "target_price": 9.5
  }'
```

#### Beispielantwort

```json
{
  "subscription": {
    "id": "mas_8c1f0e2d3b4a59687766554433221100",
    "type": "price",
    "target": "com",
    "registrar": "porkbun",
    "events": ["price.dropped"],
    "status": "active",
    "price_type": "renewal",
    "drop_percent": 10,
    "target_price": 9.5,
    "currency": "USD",
    "observed": null,
    "last_check_status": null,
    "next_check_at": "2026-10-10T12:00:00.000Z"
  }
}
```

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `subscription` | object |
| `subscription.id` | string |
| `subscription.type` | string |
| `subscription.target` | string |
| `subscription.registrar` | string \| null |
| `subscription.events[]` | string[] |
| `subscription.status` | string |
| `subscription.ssl_expiring_days` | integer \| null |
| `subscription.price_type` | string \| null |
| `subscription.drop_percent` | number \| null |
| `subscription.target_price` | number \| null |
| `subscription.currency` | string \| null |
| `subscription.record_types[]` | string[] |
| `subscription.observed` | object \| null |
| `subscription.last_check_at` | string \| null |
| `subscription.last_check_status` | string \| null |
| `subscription.last_error` | string \| null |
| `subscription.next_check_at` | string \| null |
| `subscription.created_at` | string |
| `subscription.updated_at` | string |

GET /v1/watchlist/alerts

Listet Ihre Abonnements mit den neuesten bestätigten Belegen und dem Prüfstatus, den von jedem Typ unterstützten Ereignissen, dem Prüfplan und dem Alarmpreis auf.

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `subscriptions[]` | object[] |
| `subscriptions[]` | object |
| `subscriptions[].id` | string |
| `subscriptions[].type` | string |
| `subscriptions[].target` | string |
| `subscriptions[].registrar` | string \| null |
| `subscriptions[].events[]` | string[] |
| `subscriptions[].status` | string |
| `subscriptions[].ssl_expiring_days` | integer \| null |
| `subscriptions[].price_type` | string \| null |
| `subscriptions[].drop_percent` | number \| null |
| `subscriptions[].target_price` | number \| null |
| `subscriptions[].currency` | string \| null |
| `subscriptions[].record_types[]` | string[] |
| `subscriptions[].observed` | object \| null |
| `subscriptions[].last_check_at` | string \| null |
| `subscriptions[].last_check_status` | string \| null |
| `subscriptions[].last_error` | string \| null |
| `subscriptions[].next_check_at` | string \| null |
| `subscriptions[].created_at` | string |
| `subscriptions[].updated_at` | string |
| `total` | integer |
| `available_types` | object |
| `available_types.ssl[]` | string[] |
| `available_types.price[]` | string[] |
| `available_types.dns[]` | string[] |
| `available_types.typosquat[]` | string[] |
| `policy` | object |
| `policy.credits_per_delivered_alert` | integer |
| `policy.check_credits` | integer |
| `policy.unknown_checks_charged` | boolean |
| `policy.max_subscriptions` | integer |
| `policy.max_typosquat_subscriptions` | integer |
| `policy.ssl_expiring_days[]` | integer[] |
| `policy.default_ssl_expiring_days` | integer |
| `policy.record_types[]` | string[] |
| `policy.price_types[]` | string[] |
| `policy.check_interval_minutes` | object |
| `policy.confirmation_checks` | object |

#### Beispielantwort

```json
{
  "subscriptions": [
    {
      "id": "example-id",
      "type": "ssl",
      "target": "example",
      "registrar": "example",
      "events": [
        "ssl.expiring"
      ],
      "status": "active",
      "ssl_expiring_days": 30,
      "price_type": "registration",
      "drop_percent": 0.5,
      "target_price": 0.5,
      "currency": "USD",
      "record_types": [
        "A"
      ],
      "observed": {},
      "last_check_at": "2026-08-27T12:00:00Z",
      "last_check_status": "baseline",
      "last_error": "example",
      "next_check_at": "2026-08-27T12:00:00Z",
      "created_at": "2026-08-27T12:00:00Z",
      "updated_at": "2026-08-27T12:00:00Z"
    }
  ],
  "total": 1,
  "available_types": {
    "ssl": [
      "ssl.expiring"
    ],
    "price": [
      "price.dropped"
    ],
    "dns": [
      "dns.changed"
    ],
    "typosquat": [
      "typosquat.detected"
    ]
  },
  "policy": {
    "credits_per_delivered_alert": 2,
    "check_credits": 0,
    "unknown_checks_charged": false,
    "max_subscriptions": 1,
    "check_interval_minutes": {},
    "max_typosquat_subscriptions": 1,
    "ssl_expiring_days": [
      1
    ],
    "default_ssl_expiring_days": 1,
    "record_types": [
      "A"
    ],
    "price_types": [
      "registration"
    ],
    "confirmation_checks": {}
  }
}
```

GET /v1/watchlist/alerts/:subscription_id

Ruft ein Abonnement mit den neuesten bestätigten Belegen und seinen 20 jüngsten Ereignissen ab.

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `subscription` | object |
| `subscription.id` | string |
| `subscription.type` | string |
| `subscription.target` | string |
| `subscription.registrar` | string \| null |
| `subscription.events[]` | string[] |
| `subscription.status` | string |
| `subscription.ssl_expiring_days` | integer \| null |
| `subscription.price_type` | string \| null |
| `subscription.drop_percent` | number \| null |
| `subscription.target_price` | number \| null |
| `subscription.currency` | string \| null |
| `subscription.record_types[]` | string[] |
| `subscription.observed` | object \| null |
| `subscription.last_check_at` | string \| null |
| `subscription.last_check_status` | string \| null |
| `subscription.last_error` | string \| null |
| `subscription.next_check_at` | string \| null |
| `subscription.created_at` | string |
| `subscription.updated_at` | string |
| `events[]` | object[] |
| `events[]` | object |
| `events[].id` | string |
| `events[].event_type` | string |
| `events[].detected_at` | string |
| `events[].published_at` | string \| null |
| `events[].data` | object |

#### Beispielantwort

```json
{
  "subscription": {
    "id": "example-id",
    "type": "ssl",
    "target": "example",
    "registrar": "example",
    "events": [
      "ssl.expiring"
    ],
    "status": "active",
    "ssl_expiring_days": 30,
    "price_type": "registration",
    "drop_percent": 0.5,
    "target_price": 0.5,
    "currency": "USD",
    "record_types": [
      "A"
    ],
    "observed": {},
    "last_check_at": "2026-08-27T12:00:00Z",
    "last_check_status": "baseline",
    "last_error": "example",
    "next_check_at": "2026-08-27T12:00:00Z",
    "created_at": "2026-08-27T12:00:00Z",
    "updated_at": "2026-08-27T12:00:00Z"
  },
  "events": [
    {
      "id": "example-id",
      "event_type": "ssl.expiring",
      "detected_at": "2026-08-27T12:00:00Z",
      "published_at": "2026-08-27T12:00:00Z",
      "data": {}
    }
  ]
}
```

PATCH /v1/watchlist/alerts/:subscription_id

Pausiert oder setzt ein Abonnement mit enabled fort oder ändert dessen events, ssl_expiring_days, drop_percent, target_price oder record_types. Ein neuer Zielpreis löst erst bei einer späteren Preisbewegung einen Alarm aus.

```json
{ "enabled": false }
```

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `subscription` | object |
| `subscription.id` | string |
| `subscription.type` | string |
| `subscription.target` | string |
| `subscription.registrar` | string \| null |
| `subscription.events[]` | string[] |
| `subscription.status` | string |
| `subscription.ssl_expiring_days` | integer \| null |
| `subscription.price_type` | string \| null |
| `subscription.drop_percent` | number \| null |
| `subscription.target_price` | number \| null |
| `subscription.currency` | string \| null |
| `subscription.record_types[]` | string[] |
| `subscription.observed` | object \| null |
| `subscription.last_check_at` | string \| null |
| `subscription.last_check_status` | string \| null |
| `subscription.last_error` | string \| null |
| `subscription.next_check_at` | string \| null |
| `subscription.created_at` | string |
| `subscription.updated_at` | string |

#### Beispielantwort

```json
{
  "subscription": {
    "id": "example-id",
    "type": "ssl",
    "target": "example",
    "registrar": "example",
    "events": [
      "ssl.expiring"
    ],
    "status": "active",
    "ssl_expiring_days": 30,
    "price_type": "registration",
    "drop_percent": 0.5,
    "target_price": 0.5,
    "currency": "USD",
    "record_types": [
      "A"
    ],
    "observed": {},
    "last_check_at": "2026-08-27T12:00:00Z",
    "last_check_status": "baseline",
    "last_error": "example",
    "next_check_at": "2026-08-27T12:00:00Z",
    "created_at": "2026-08-27T12:00:00Z",
    "updated_at": "2026-08-27T12:00:00Z"
  }
}
```

DELETE /v1/watchlist/alerts/:subscription_id

Löscht ein Abonnement. Noch nicht gesendete Ereignisse werden verworfen und nie berechnet.

#### Antwortfelder

| Feld | Typ |
| --- | --- |
| `id` | string |
| `deleted` | boolean |

#### Beispielantwort

```json
{
  "id": "example-id",
  "deleted": true
}
```

#### Beispiel für ein dns.changed-Ereignis

```json
{
  "id": "evt_2b9c8d7e6f5a4b3c2d1e0f9a8b7c6d5e",
  "type": "dns.changed",
  "created": "2026-10-10T12:15:00.000Z",
  "data": {
    "domain": "partner-shop.example",
    "changes": [
      {
        "record_type": "MX",
        "before": ["10 mx1.mail.example"],
        "after": ["10 mx.other-mail.example"],
        "added": ["10 mx.other-mail.example"],
        "removed": ["10 mx1.mail.example"],
        "first_seen_at": "2026-10-10T12:00:00.000Z"
      }
    ],
    "confirmed_checks": 2,
    "confirmed_at": "2026-10-10T12:15:00.000Z",
    "subscription_id": "mas_8c1f0e2d3b4a59687766554433221100"
  }
}
```
