> 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/it/docs/watchlist

# Watchlist Dominio

API DomScan: Monitora i domini per modifiche di scadenza e disponibilità. Ricevi avvisi quando i domini registrati diventano disponibili.

Monitora i domini per modifiche di scadenza e disponibilità. Ricevi avvisi quando i domini registrati diventano disponibili.

**Monitoraggio**

Accedi con Google per accedere al tuo elenco di vigilanza dei domini personalizzato con avvisi email, webhook e altri.

Ricevi notifiche tramite e-mail, Telegram, Slack, Discord o Webhook.

[Monitoraggio](https://domscan.net/it/monitor)
[Impostazioni](https://domscan.net/it/monitor/settings)
[Monitoraggio del marchio](https://domscan.net/it/brand-monitor-api)

GET /v1/watchlist

Ottieni tutti i domini nella tua watchlist.

#### Risposta di esempio

```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"
                }
```

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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

Aggiungi un dominio alla tua watchlist.

#### Corpo della richiesta

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

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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 |

#### Risposta di esempio

```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

Rimuovi dalla lista di controllo

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

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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 |

#### Risposta di esempio

```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

In scadenza a breve

#### Risposta di esempio

```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
                }
```

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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

Disponibile

Paginato: invia `limit` (da 1 a 100) e passa `next_cursor` come `cursor`. Le risposte includono `has_more` e `next_cursor`. Senza `limit` viene restituita l'intera lista. Vedi la [paginazione](https://domscan.net/docs/response-format#pagination).

#### Risposta di esempio

```json
{
                "available_domains": [
                {
                "domain": "startupdrop.ai",
                "status": "available",
                "last_checked": "2026-04-14T08:30:00Z",
                "check_count": 18
                }
                ],
                "total": 1,
                "message": "Questi domini dalla tua watchlist sono ora disponibili per la registrazione!"
                }
```

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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 |

### Webhook per gli avvisi

Ricevi gli avvisi di monitoraggio come richieste POST JSON firmate sui tuoi endpoint HTTPS. Gestisci gli endpoint con una chiave API oppure dalle impostazioni di Monitoraggio dopo aver effettuato l’accesso.

#### Tipi di evento

| Monitoraggio | Eventi |
| --- | --- |
| Disponibilità, scadenza e stato della watchlist | `domain.available`, `domain.expiring_30`, `domain.expiring_14`, `domain.expiring_7`, `domain.expiring_1`, `domain.status_change` |
| Monitoraggio delle modifiche dei domini | `domain.dns_changed`, `domain.certificate_changed`, `domain.registration_changed`, `domain.hosting_changed`, `domain.technology_changed`, `domain.email_security_changed` |
| Monitoraggio del brand (nuove registrazioni simili) | `brand.threat_detected` |
| Sottoscrizioni di avviso della lista di monitoraggio (certificati, prezzi, record DNS, domini simili) | `ssl.expiring`, `ssl.expired`, `ssl.changed`, `price.dropped`, `dns.changed`, `typosquat.detected` |
| Job asincroni (gratuiti, solo per i job creati con notify_webhook) | `job.completed`, `job.failed`, `job.cancelled` |
| Evento di test (gratuito) | `webhook.test` |

[Leggi la guida all'API degli avvisi webhook](https://domscan.net/it/webhook-alerts-api)

#### Prezzi

Ogni avviso consegnato a un endpoint con una risposta 2xx costa 2 crediti. I crediti vengono riservati subito prima del primo tentativo e rimborsati se la consegna non riesce, viene annullata o l’endpoint viene eliminato. Gli eventi di test sono gratuiti. Se il saldo è insufficiente, l’avviso viene saltato e non viene addebitato nulla.

POST /v1/webhooks

Registra un endpoint HTTPS. L’URL deve risolversi in un indirizzo pubblico. Ometti events, oppure passa ["*"], per ricevere ogni tipo di evento. Il segreto di firma viene restituito solo in questa risposta.

#### Richiesta di esempio

```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"]
  }'
```

#### Risposta di esempio

```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_..."
}
```

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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

Elenca i tuoi endpoint con stato, eventi sottoscritti, numero di errori e ultimi quattro caratteri del segreto, insieme ai tipi di evento disponibili e alla policy di consegna.

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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 |

#### Risposta di esempio

```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

Metti in pausa o riattiva un endpoint, cambia i suoi eventi o modifica la descrizione. Riattivare un endpoint azzera il suo contatore di errori.

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

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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 |

#### Risposta di esempio

```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

Elimina un endpoint. Le consegne in sospeso vengono annullate e gli eventuali crediti riservati vengono rimborsati.

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `id` | string |
| `deleted` | boolean |

#### Risposta di esempio

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

POST /v1/webhooks/:webhook_id/test

Invia un evento webhook.test gratuito e restituisce il risultato. Gli eventi di test non vengono ritentati e non contano ai fini della pausa automatica.

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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 |

#### Risposta di esempio

```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

Sostituisce il segreto di firma di un endpoint, anche se è in pausa. Il nuovo segreto viene restituito solo in questa risposta. Per le 24 ore successive, ogni consegna è firmata sia con il nuovo segreto sia con quello precedente. La rotazione è gratuita.

#### Richiesta di esempio

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

#### Risposta di esempio

```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
}
```

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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

#### Parametri di query

| Parametro | Tipo | Descrizione |
| --- | --- | --- |
| limit | integer | Numero di consegne da restituire, da 1 a 100. Predefinito 50. |

Elenca le consegne recenti con stato, tentativi, risposta HTTP, crediti addebitati e rimborsi. I record delle consegne vengono conservati per 30 giorni.

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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 |

#### Risposta di esempio

```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"
    }
  ]
}
```

#### Formato della richiesta

Ogni avviso è una richiesta POST con un corpo JSON. Il campo id resta invariato per un evento ed è ripetuto in X-DomScan-Event-Id, così puoi ignorare i nuovi tentativi già elaborati.

```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"
  }
}
```

#### Verifica delle firme

Calcola un HMAC-SHA256 del valore di X-DomScan-Timestamp, un punto e il corpo grezzo della richiesta, usando il segreto del tuo endpoint. Confronta il risultato con X-DomScan-Signature in tempo costante e rifiuta le richieste con un timestamp più vecchio di 5 minuti.

L'esempio sotto verifica ogni firma presente nell'intestazione, quindi continua a funzionare anche durante la rotazione di un segreto.

```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);
  });
}
```

#### Ruotare un segreto di firma

Durante il periodo di tolleranza di 24 ore dopo una rotazione, X-DomScan-Signature contiene due firme separate da una virgola, con il nuovo segreto per primo, come mostrato sotto. Accetta una richiesta quando una qualsiasi voce corrisponde a un segreto di cui ti fidi, distribuisci il nuovo segreto e poi smetti di fidarti del vecchio. Ruotando di nuovo durante il periodo di tolleranza, il segreto precedente più vecchio viene ritirato subito.

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

#### Nuovi tentativi e pausa automatica

Restituisci qualsiasi stato 2xx entro 8 secondi per confermare un avviso. Reindirizzamenti, altri codici di stato e timeout vengono ritentati con backoff esponenziale (1, 4, 16, 64 e 256 minuti), fino a 6 tentativi in totale. Dopo 15 tentativi consecutivi non riusciti l’endpoint viene messo in pausa; riattivalo con PATCH o dalle impostazioni di Monitoraggio.

Quando un endpoint viene messo in pausa dopo errori ripetuti, il proprietario dell'endpoint riceve un'e-mail con l'host dell'endpoint, l'ultimo errore e un link alle impostazioni di Monitor. Correggi l'endpoint, poi riattivalo dalle impostazioni di Monitor o con PATCH e enabled impostato su true. La riattivazione azzera il conteggio degli errori.

### Sottoscrizioni di avviso della lista di monitoraggio

Sottoscrivi un elemento monitorato agli avvisi che desideri. Ogni sottoscrizione monitora un hostname, un TLD o un dominio di marchio, e tu scegli quali eventi ricevere. Gli eventi vengono inviati ai tuoi endpoint webhook firmati e ai tuoi avvisi nell'app.

| Tipo | Eventi | Quando si attiva |
| --- | --- | --- |
| `ssl` | `ssl.expiring`, `ssl.expired`, `ssl.changed` | ssl.expiring quando il certificato servito da un hostname scade entro ssl_expiring_days (30, 14, 7 o 1, predefinito 14), una volta per certificato e soglia. ssl.expired una volta per ogni certificato scaduto. ssl.changed quando una nuova impronta del certificato viene rilevata in due controlli consecutivi; il payload indica se l'emittente è cambiato. |
| `price` | `price.dropped` | Quando il prezzo di registrazione o di rinnovo di un TLD, presso un registrar o presso il più economico, scende di almeno drop_percent rispetto al prezzo più alto dall'ultimo avviso, oppure raggiunge target_price o meno. I prezzi provengono dallo snapshot giornaliero dei prezzi, e ogni snapshot viene valutato una sola volta. |
| `dns` | `dns.changed` | Quando un tipo di record monitorato (A, AAAA, MX, NS, TXT o CNAME) di qualsiasi hostname restituisce la stessa nuova risposta in due interrogazioni confermate consecutive. |
| `typosquat` | `typosquat.detected` | Quando un dominio simile al dominio di marchio viene registrato per la prima volta, confermato da un'interrogazione di registrazione autorevole. Il primo passaggio completo sui domini simili stabilisce la base di riferimento e non genera avvisi. |

Solo prove esatte attivano un evento. Un handshake TLS non riuscito, un'interrogazione DNS che fallisce o va in timeout, un prezzo mancante e un'interrogazione bloccata o limitata in frequenza sono sconosciuti: non cambiano nulla e non generano mai un avviso né un addebito. Il primo controllo confermato di una sottoscrizione stabilisce la sua base di riferimento.

I controlli vengono eseguiti automaticamente: certificati ogni 12 ore, record DNS ogni ora (con un controllo di conferma 15 minuti dopo la prima rilevazione di una modifica), prezzi ogni 2 ore rispetto allo snapshot giornaliero e domini simili in batch da 30 con un passaggio completo ogni giorno. Dopo un controllo sconosciuto, il tentativo successivo avviene prima.

Creare sottoscrizioni ed eseguire controlli è gratuito. Ogni avviso consegnato a un endpoint webhook con risposta 2xx costa 2 crediti, come ogni altro avviso di monitoraggio. Le consegne non riuscite vengono rimborsate e un account può avere fino a 100 sottoscrizioni, 20 delle quali per domini simili.

POST /v1/watchlist/alerts

Crea una sottoscrizione. type decide quali campi si applicano: domain per ssl, dns e typosquat; tld, registrar, price_type, drop_percent e target_price per price. I campi che non si applicano al tipo vengono rifiutati.

#### Corpo della richiesta

| Parametro | Tipo | Descrizione |
| --- | --- | --- |
| type | string | ssl, price, dns o typosquat. Obbligatorio. |
| domain | string | Hostname da monitorare per ssl e dns, oppure il dominio di marchio per typosquat. |
| tld | string | TLD da monitorare per price, ad esempio com. |
| registrar | string | ID del registrar per price. Omettilo per monitorare il registrar più economico. |
| events | string[] | Eventi da ricevere. Per impostazione predefinita, tutti gli eventi del tipo. |
| ssl_expiring_days | integer | Giorni prima della scadenza per ssl.expiring: 30, 14, 7 o 1. Predefinito 14. |
| price_type | string | registration (predefinito) o renewal. |
| drop_percent | number | Avviso in caso di calo di almeno questa percentuale, da 1 a 95. |
| target_price | number | Avviso quando il prezzo in USD raggiunge questo importo o meno. Una sottoscrizione di prezzo richiede drop_percent, target_price o entrambi. |
| record_types | string[] | Tipi di record da monitorare per dns. Per impostazione predefinita A, AAAA, MX, NS, TXT e CNAME. |

#### Richiesta di esempio

```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
  }'
```

#### Risposta di esempio

```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"
  }
}
```

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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

Elenca le tue sottoscrizioni con le ultime prove confermate e lo stato dei controlli, gli eventi supportati da ciascun tipo, la pianificazione dei controlli e il prezzo dell'avviso.

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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 |

#### Risposta di esempio

```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

Recupera una sottoscrizione con le ultime prove confermate e i suoi 20 eventi più recenti.

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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 |

#### Risposta di esempio

```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

Mette in pausa o riprende una sottoscrizione con enabled, oppure ne modifica events, ssl_expiring_days, drop_percent, target_price o record_types. Un nuovo prezzo obiettivo genera un avviso solo in caso di una successiva variazione di prezzo.

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

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `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 |

#### Risposta di esempio

```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

Elimina una sottoscrizione. Gli eventi che non ha ancora inviato vengono scartati e mai addebitati.

#### Campi della risposta

| Campo | Tipo |
| --- | --- |
| `id` | string |
| `deleted` | boolean |

#### Risposta di esempio

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

#### Esempio di evento dns.changed

```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"
  }
}
```
