DNS Doctor Watch API

The watch API from DNS Doctor — 5 operation(s) for watch.

Operations 5

POST /watch/propagation Create Propagation Watch #
POST /watch/ping Create Ping Watch #
POST /watch/record Create Record Watch #
POST /watch/lookalikes Create Lookalikes Watch #
GET /watch/{watch_id} Poll Watch #

Work with this as data

Every API here is available over the APIs.io API and to AI agents over MCP.

MCP server

One button, every client — Claude, Cursor, VS Code and the rest.

https://apis.io/mcp

Tools for apis

7 MCP tools reach this
  • find_apisBrowse and filter every API in the catalog.
  • get_api_artifactsOne API's artifacts, grouped by type.
  • get_openapiThe primary OpenAPI for this API.
  • find_similar_apisAPIs that look like this one.
  • apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.
  • resolveTurn a domain, URL or GitHub org into the provider it belongs to.
  • find_cohortsEvery scored population of providers in the catalog.
All 92 tools →

Call it yourself

curl for this page
This API
curl "https://apis.io/api/v1/apis/dnsdoctor-dev:dnsdoctor-dev-watch-api"
All apis
curl "https://apis.io/api/v1/apis?limit=25"

Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.

Get an API key

Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.

A second provider on the same verified email joins the account you already have.

OpenAPI Specification

dnsdoctor-dev-watch-api-openapi.yml Raw ↑
openapi: 3.2.0
info:
  title: Dnsdoctor Dev Watch API
  version: '1'
  contact:
    name: DNS Doctor
    email: hello@dnsdoctor.dev
  x-guidance: 'DNS Doctor scans, fixes and verifies a domain''s DNS (SPF, DMARC, DKIM, propagation, MX, DNS health, blacklists, domain/SSL expiry) and returns deterministic verdicts plus copy-paste fix records from a validating engine — never a guessed record. Scan with POST /scan or GET /report/{domain}; read verdicts failing-first (''temperror'' is transient, not a failure; a report with not_registered: true means the domain does not resolve, so propose nothing for it). Present any returned record VERBATIM and never compose one yourself; SPF is diagnose-only. A human approves every DNS change — nothing is applied automatically. Anonymous calls are free up to a per-caller limit; past it the API answers 402 (x402, USDC on Base, $0.01 per call). POST /bulk-scan is paid on every call at $0.005 per domain; POST /bulk-registration (RDAP) at $0.002 per domain; POST /bulk-propagation at $0.008 per check; POST /bulk-dmarc-reports parses DMARC aggregate reports at $0.005 per report; POST /watch/record watches one DNS record for up to 24 hours at $0.10; POST /watch/lookalikes watches a domain''s lookalike names hourly for one fixed 24-hour day at $0.50. The five token-gated reads (GET /domains, /alerts, /readiness, /lookalikes and /email-tests) need a DNS Doctor API token the account owner mints at /dashboard/settings — never ask a user to paste one.'
  description: 'Operations tagged watch across 2 of this provider''s published API definitions: dnsdoctor-dev-openapi.json, dnsdoctor-dev-openapi.json. Each path carries the servers of the definition it was published in.'
servers:
- url: https://dnsdoctor.dev/api/v1
tags:
- name: watch
paths:
  /watch/propagation:
    post:
      tags:
      - watch
      summary: Create Propagation Watch
      description: 'Buy a propagation watch: one name, six vantage points, up to 24 hours.


        Anonymous; **402 on every call**. Gate order is the contract — inert 503,

        then the 422 on a malformed target, then the fleet-wide caps'' 429, and only

        then the lane. Everything that can refuse refuses before any money moves, and

        the payment is settled only once the row is committed.'
      operationId: create_propagation_watch_watch_propagation_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WatchPropagationIn'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WatchCreated'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '402':
          description: Payment required — retry with an x402 payment header.
      x-payment-info:
        payTo: '0x6fDc1f22520151DfBc6273d6C48E413D434C7916'
        price:
          mode: dynamic
          currency: USD
          min: '0.004167'
          max: '0.1'
        protocols:
        - x402: {}
        network: base
    servers:
    - url: https://dnsdoctor.dev/api/v1
  /watch/ping:
    post:
      tags:
      - watch
      summary: Create Ping Watch
      description: 'Buy an HTTP ping watch: one URL, five regions on four continents, up to 24 hours.


        Same gate order as the propagation create, with one gate more: the URL is put

        through the SSRF seam (structural rules, then resolve-and-pin) BEFORE the lane

        opens, so a target we would never dispatch five hosts at is a free 422. The

        per-check re-resolve in `watch/ping.py` is what closes the rebinding window

        afterwards — this pre-flight only keeps an unbuyable target from being sold.'
      operationId: create_ping_watch_watch_ping_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WatchPingIn'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WatchCreated'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '402':
          description: Payment required — retry with an x402 payment header.
      x-payment-info:
        payTo: '0x6fDc1f22520151DfBc6273d6C48E413D434C7916'
        price:
          mode: dynamic
          currency: USD
          min: '0.004167'
          max: '0.25'
        protocols:
        - x402: {}
        network: base
    servers:
    - url: https://dnsdoctor.dev/api/v1
  /watch/record:
    post:
      tags:
      - watch
      summary: Create Record Watch
      description: 'Buy a DNS record watch: one record, read authoritatively, up to 24 hours.


        Anonymous; **402 on every call**. The propagation create''s gate order: inert

        503, the 422 on a malformed target, the fleet-wide caps'' 429, then the lane —

        everything that can refuse refuses before any money moves.'
      operationId: create_record_watch_watch_record_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WatchRecordIn'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WatchCreated'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '402':
          description: Payment required — retry with an x402 payment header.
      x-payment-info:
        payTo: '0x6fDc1f22520151DfBc6273d6C48E413D434C7916'
        price:
          mode: dynamic
          currency: USD
          min: '0.004167'
          max: '0.1'
        protocols:
        - x402: {}
        network: base
    servers:
    - url: https://dnsdoctor.dev/api/v1
  /watch/lookalikes:
    post:
      tags:
      - watch
      summary: Create Lookalikes Watch
      description: 'Buy one day of hourly lookalike checks on a domain: a baseline, then the changes.


        Anonymous; **402 on every call**, $0.50 for the fixed 24-hour day. The record

        create''s gate order: inert 503, the 422 on a malformed domain, the caps'' 429

        (the lookalike fleet cap included), then the lane — everything that can

        refuse refuses before any money moves.'
      operationId: create_lookalikes_watch_watch_lookalikes_post
      requestBody:
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/WatchLookalikesIn'
        required: true
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WatchCreated'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
        '402':
          description: Payment required — retry with an x402 payment header.
      x-payment-info:
        payTo: '0x6fDc1f22520151DfBc6273d6C48E413D434C7916'
        price:
          mode: dynamic
          currency: USD
          min: '0.5'
          max: '0.5'
        protocols:
        - x402: {}
        network: base
    servers:
    - url: https://dnsdoctor.dev/api/v1
  /watch/{watch_id}:
    get:
      tags:
      - watch
      summary: Poll Watch
      description: 'Read one bought watch. Free, per-handle rate-limited, 404 on anything else.


        `before` pages backwards through the observations (newest first, 50 a page);

        the response''s `next_before` is non-null exactly when older ones remain.'
      operationId: poll_watch_watch__watch_id__get
      parameters:
      - name: watch_id
        in: path
        required: true
        schema:
          type: string
          title: Watch Id
      - name: before
        in: query
        required: false
        schema:
          anyOf:
          - type: string
            maxLength: 40
          - type: 'null'
          title: Before
      responses:
        '200':
          description: Successful Response
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/WatchStatus'
        '422':
          description: Validation Error
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/HTTPValidationError'
      security: []
    servers:
    - url: https://dnsdoctor.dev/api/v1
components:
  schemas:
    HTTPValidationError:
      properties:
        detail:
          items:
            $ref: '#/components/schemas/ValidationError'
          type: array
          title: Detail
      type: object
      title: HTTPValidationError
    WatchRecordIn:
      properties:
        domain:
          type: string
          maxLength: 253
          title: Domain
        kind:
          type: string
          maxLength: 16
          title: Kind
          description: 'Which record to watch: spf, dmarc, txt, mx, cname, a or aaaa. The watch reports `met` when the authoritative answer set differs from the first one recorded — a name whose A/AAAA answers legitimately rotate (an ELB/ALB or GSLB hostname) meets on the rotation, not on your change.'
        host:
          anyOf:
          - type: string
            maxLength: 200
          - type: 'null'
          title: Host
          description: A label under the domain (txt, cname, a and aaaa only), e.g. `www`.
        window_hours:
          type: number
          maximum: 24.0
          minimum: 1.0
          title: Window Hours
          default: 24
      type: object
      required:
      - domain
      - kind
      title: WatchRecordIn
      description: 'The record watch''s terms. Target validation is `resolve_query`''s.


        `window_hours` is bounded at the MODEL, the propagation watch''s reasoning:

        the published `input_schema` says 1 to 24, and a sub-hour window would

        pro-rate toward a `$0` offer on an always-paid surface.'
    WatchLookalikesIn:
      properties:
        domain:
          type: string
          maxLength: 253
          title: Domain
          description: The domain whose lookalikes to watch.
      type: object
      required:
      - domain
      title: WatchLookalikesIn
      description: The lookalike day's one term. Validation is `normalize_domain`'s.
    ValidationError:
      properties:
        loc:
          items:
            anyOf:
            - type: string
            - type: integer
          type: array
          title: Location
        msg:
          type: string
          title: Message
        type:
          type: string
          title: Error Type
        input:
          title: Input
        ctx:
          type: object
          title: Context
      type: object
      required:
      - loc
      - msg
      - type
      title: ValidationError
    WatchPingIn:
      properties:
        url:
          type: string
          maxLength: 2000
          title: Url
          description: The URL to check. It is fetched over the shared probe fleet and stored for the life of the watch, so it must not carry a secret — no token, key or signature in the path or query.
        cadence_seconds:
          type: integer
          title: Cadence Seconds
          default: 300
        window_hours:
          type: number
          maximum: 24.0
          minimum: 1.0
          title: Window Hours
          default: 24
      type: object
      required:
      - url
      title: WatchPingIn
      description: 'The ping watch''s terms. The URL itself is validated in `watch/targets.py`.


        `cadence_seconds` is bounded to the two cadences we sell at the MODEL, so an

        unsold cadence is refused during parse rather than silently priced as the

        cheap one. The HTTP method is deliberately NOT a buyer knob: the probe fleet

        issues a `HEAD`, which is the smallest request that answers "does this URL

        still respond" and the least useful thing to aim at a third party.'
    WatchCreated:
      properties:
        watch_id:
          type: string
          title: Watch Id
        kind:
          type: string
          title: Kind
        target:
          type: string
          title: Target
        record_type:
          anyOf:
          - type: string
          - type: 'null'
          title: Record Type
        expected_value:
          anyOf:
          - type: string
          - type: 'null'
          title: Expected Value
        state:
          type: string
          title: State
        cadence_seconds:
          type: integer
          title: Cadence Seconds
        window_hours:
          type: number
          title: Window Hours
        price_usd:
          type: string
          title: Price Usd
        created_at:
          type: string
          title: Created At
        expires_at:
          type: string
          title: Expires At
        results_expire_at:
          type: string
          title: Results Expire At
        poll_url:
          type: string
          title: Poll Url
      type: object
      required:
      - watch_id
      - kind
      - target
      - record_type
      - expected_value
      - state
      - cadence_seconds
      - window_hours
      - price_usd
      - created_at
      - expires_at
      - results_expire_at
      - poll_url
      title: WatchCreated
      description: 'The minted handle plus the terms it was sold on.


        Deterministic throughout: an echo of what the buyer asked for, the price that

        was settled, and where to poll. No prose, no record, no prescription — the

        D94 invariant carries onto every watch surface.'
    WatchStatus:
      properties:
        kind:
          type: string
          title: Kind
        target:
          type: string
          title: Target
        state:
          type: string
          title: State
        params:
          additionalProperties: true
          type: object
          title: Params
        created_at:
          type: string
          title: Created At
        expires_at:
          type: string
          title: Expires At
        results_expire_at:
          type: string
          title: Results Expire At
        last_checked_at:
          anyOf:
          - type: string
          - type: 'null'
          title: Last Checked At
        checks:
          type: integer
          title: Checks
        verdict:
          anyOf:
          - type: string
          - type: 'null'
          title: Verdict
        observations:
          items:
            additionalProperties: true
            type: object
          type: array
          title: Observations
        next_before:
          anyOf:
          - type: string
          - type: 'null'
          title: Next Before
      type: object
      required:
      - kind
      - target
      - state
      - params
      - created_at
      - expires_at
      - results_expire_at
      - last_checked_at
      - checks
      - verdict
      - observations
      title: WatchStatus
      description: 'A watch''s state and everything observed so far.


        Deterministic throughout, and a **relay**: `verdict` is the verdict the last

        observation was recorded with, never re-derived here — the runner writes it

        from D94''s own `derive_verdict`, so the poll surface cannot drift from the

        on-demand check''s rules (a faulted probe is an unreached vantage, never a

        negative reading, and fewer than three contributing vantage points degrade

        the verdict to `unknown`).


        `observations` are relayed verbatim as the runner recorded them, minus the

        `_`-prefixed internal state `observation_page` strips. No prose, no

        composed record, no prescription-shaped field at any depth — the D94

        invariant carries onto every watch surface.'
    WatchPropagationIn:
      properties:
        name:
          type: string
          maxLength: 1012
          title: Name
        record_type:
          type: string
          title: Record Type
          default: A
        expected_value:
          anyOf:
          - type: string
            maxLength: 1024
          - type: 'null'
          title: Expected Value
          description: The value every vantage point must agree on for the watch to report `met`. Without it, `met` means the grid agreed on an answer set DIFFERENT from the first one recorded — a name whose answers legitimately rotate (an ELB/ALB or GSLB hostname) meets on the rotation, not on your change; pass the value you are waiting for.
        window_hours:
          type: number
          maximum: 24.0
          minimum: 1.0
          title: Window Hours
          default: 24
      type: object
      required:
      - name
      title: WatchPropagationIn
      description: 'The propagation watch''s terms. Validation is `normalize_probe_name`''s.


        `window_hours` is bounded at the MODEL so an out-of-range window is refused

        during parse — the metered-422 handler covers that leg, and a bound checked

        later would price a window we do not sell. The bound is the one the surface''s

        published `input_schema` advertises (1 to 24), not a wider one: a machine

        contract that under-states what it accepts is drift, and a sub-hour window

        would pro-rate toward a `$0` offer on an always-paid surface.'
  securitySchemes:
    apiKey:
      type: http
      scheme: bearer
      description: 'A DNS Doctor API token: `Authorization: Bearer dnsd_…`.'
x-refined-from:
- dnsdoctor-dev-openapi.json
- dnsdoctor-dev-openapi.json
x-discovery:
  ownershipProofs:
  - '0xeb49cc7d9ac04add7ee0fbe51bc806b684ebdb6bfcdc8c2d7aa7eebc02032b93403764b0986d036ed68ad7e5914edc5403209f19806f4a87e15b1a5bdb73e9ab1b'