DNS Doctor · Schema

ScanReportOut

What a scan endpoint actually returns: the report plus the A8 hand-off. Declared as a response model rather than by widening `ScanReport` itself: `ScanReport` round-trips through the persisted `report_json`, and a field there would be frozen into stored rows. This subclass is never handed to `save_report`, so the non-persistence property is untouched while `/api/v1/openapi.json` documents the real response shape — which the agent channel (`/llms-full.txt`, U7's api-catalog) depends on being accurate.

DNSDNS ToolsEmail AuthenticationWeb Application

Properties

Name Type Description
domain string
checks array
scanned_at string
explanations_pending boolean
dns_provider_key object
not_registered boolean
agent_scanned boolean
registration object
next_steps object
sender_requirements object
indexable boolean Thin-page shield: worth indexing only if mail-capable AND substantive. Mail-capable = any of mx/spf/dmarc observed a record; substantive = at least `_MIN_RESOLVED_CHECKS` checks resolved (non-TEMPERRO
dns_provider object The serialized assist-only NS provider, looked up by key at read time. Mirrors the domain-row `dns_provider` object: the key is stored, the full object (display name, console URL, guide anchor, collis
View JSON Schema on GitHub

JSON Schema

dnsdoctor-dev-scan-report-out-schema.json Raw ↑
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/dnsdoctor-dev/json-schema/dnsdoctor-dev-scan-report-out-schema.json",
  "title": "ScanReportOut",
  "description": "What a scan endpoint actually returns: the report plus the A8 hand-off.\n\nDeclared as a response model rather than by widening `ScanReport` itself:\n`ScanReport` round-trips through the persisted `report_json`, and a field\nthere would be frozen into stored rows. This subclass is never handed to\n`save_report`, so the non-persistence property is untouched while\n`/api/v1/openapi.json` documents the real response shape — which the agent\nchannel (`/llms-full.txt`, U7's api-catalog) depends on being accurate.",
  "x-generated": "2026-11-05",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/dnsdoctor-dev-openapi.json#/components/schemas/ScanReportOut",
  "properties": {
    "domain": {
      "type": "string",
      "title": "Domain"
    },
    "checks": {
      "items": {
        "$ref": "#/$defs/CheckResult"
      },
      "type": "array",
      "title": "Checks"
    },
    "scanned_at": {
      "type": "string",
      "format": "date-time",
      "title": "Scanned At"
    },
    "explanations_pending": {
      "type": "boolean",
      "title": "Explanations Pending",
      "default": false
    },
    "dns_provider_key": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Dns Provider Key"
    },
    "not_registered": {
      "type": "boolean",
      "title": "Not Registered",
      "default": false
    },
    "agent_scanned": {
      "type": "boolean",
      "title": "Agent Scanned",
      "default": false
    },
    "registration": {
      "anyOf": [
        {
          "$ref": "#/$defs/Registration"
        },
        {
          "type": "null"
        }
      ]
    },
    "next_steps": {
      "anyOf": [
        {
          "$ref": "#/$defs/NextSteps"
        },
        {
          "type": "null"
        }
      ]
    },
    "sender_requirements": {
      "anyOf": [
        {
          "$ref": "#/$defs/SenderRequirements"
        },
        {
          "type": "null"
        }
      ]
    },
    "indexable": {
      "type": "boolean",
      "title": "Indexable",
      "description": "Thin-page shield: worth indexing only if mail-capable AND substantive.\n\nMail-capable = any of mx/spf/dmarc observed a record; substantive = at\nleast `_MIN_RESOLVED_CHECKS` checks resolved (non-TEMPERROR). NXDOMAIN,\nparked, and garbage domains fail the first gate; half-broken scans fail\nthe second. Non-indexable reports stay fully viewable — they are only\nnoindex + sitemap-excluded. Serializes into `report_json`; older\npersisted JSON without the key still validates (computed on read).",
      "readOnly": true
    },
    "dns_provider": {
      "anyOf": [
        {
          "additionalProperties": {
            "anyOf": [
              {
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "type": "object"
        },
        {
          "type": "null"
        }
      ],
      "title": "Dns Provider",
      "description": "The serialized assist-only NS provider, looked up by key at read time.\n\nMirrors the domain-row `dns_provider` object: the key is stored, the full\nobject (display name, console URL, guide anchor, collision note) is\nresolved from the `DNS_PROVIDERS` table on serialize, so a URL/copy fix\nnever needs a re-scan. A key that no longer exists in the table (or None)\nserializes as None — the report page then omits the provider mention.",
      "readOnly": true
    }
  },
  "type": "object",
  "required": [
    "domain",
    "checks",
    "indexable",
    "dns_provider"
  ],
  "$defs": {
    "CachedLookalikes": {
      "properties": {
        "summary": {
          "type": "string",
          "title": "Summary"
        },
        "resolving": {
          "type": "integer",
          "title": "Resolving"
        },
        "accepts_mail": {
          "type": "integer",
          "title": "Accepts Mail"
        },
        "checked_at": {
          "type": "string",
          "title": "Checked At"
        }
      },
      "type": "object",
      "required": [
        "summary",
        "resolving",
        "accepts_mail",
        "checked_at"
      ],
      "title": "CachedLookalikes",
      "description": "A still-fresh `check_lookalikes` result's counts — read from its cache, never probed."
    },
    "CheckResult": {
      "properties": {
        "check": {
          "type": "string",
          "title": "Check"
        },
        "status": {
          "$ref": "#/$defs/CheckStatus"
        },
        "title": {
          "type": "string",
          "title": "Title"
        },
        "raw": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Raw"
        },
        "explanation": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Explanation"
        },
        "fix_record": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Fix Record"
        },
        "details": {
          "anyOf": [
            {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "title": "Details"
        },
        "unknown": {
          "anyOf": [
            {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "title": "Unknown"
        },
        "withheld_code": {
          "anyOf": [
            {
              "type": "string",
              "const": "hosted"
            },
            {
              "type": "null"
            }
          ],
          "title": "Withheld Code"
        }
      },
      "type": "object",
      "required": [
        "check",
        "status",
        "title"
      ],
      "title": "CheckResult",
      "description": "The outcome of one check (e.g. SPF, DMARC, expiry)."
    },
    "CheckStatus": {
      "type": "string",
      "enum": [
        "pass",
        "warn",
        "fail",
        "info",
        "temperror"
      ],
      "title": "CheckStatus"
    },
    "LookalikesPointer": {
      "properties": {
        "tool": {
          "type": "string",
          "const": "check_lookalikes",
          "title": "Tool",
          "default": "check_lookalikes"
        },
        "url": {
          "type": "string",
          "title": "Url"
        },
        "cached": {
          "anyOf": [
            {
              "$ref": "#/$defs/CachedLookalikes"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "type": "object",
      "required": [
        "url"
      ],
      "title": "LookalikesPointer",
      "description": "D130: the scan's pointer to the separate `check_lookalikes` call.\n\nFilled by `api/lookalike_pointer.py` on the AGENT surfaces only (MCP\n`scan_domain`/`get_report`, v1 `/report` + `/scan`). The web `GET /report`\nthat feeds the indexable `/scan/[domain]` page never fills it (the D128\nno-teaser rule), so it serializes there as null."
    },
    "NextSteps": {
      "properties": {
        "summary": {
          "type": "string",
          "title": "Summary"
        },
        "report_url": {
          "type": "string",
          "title": "Report Url"
        },
        "monitor_url": {
          "type": "string",
          "title": "Monitor Url"
        },
        "signup_url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Signup Url"
        },
        "email_test_url": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Email Test Url"
        },
        "lookalikes": {
          "anyOf": [
            {
              "$ref": "#/$defs/LookalikesPointer"
            },
            {
              "type": "null"
            }
          ]
        }
      },
      "type": "object",
      "required": [
        "summary",
        "report_url",
        "monitor_url"
      ],
      "title": "NextSteps",
      "description": "The hand-off block's wire shape (documentation only — see `build_next_steps`)."
    },
    "Provider": {
      "properties": {
        "key": {
          "type": "string",
          "title": "Key"
        },
        "name": {
          "type": "string",
          "title": "Name"
        },
        "source_url": {
          "type": "string",
          "title": "Source Url"
        },
        "bulk_threshold": {
          "type": "string",
          "title": "Bulk Threshold"
        }
      },
      "type": "object",
      "required": [
        "key",
        "name",
        "source_url",
        "bulk_threshold"
      ],
      "title": "Provider"
    },
    "Registration": {
      "properties": {
        "registrar": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Registrar"
        },
        "registrar_iana_id": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Registrar Iana Id"
        },
        "registered_at": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time"
            },
            {
              "type": "null"
            }
          ],
          "title": "Registered At"
        },
        "updated_at": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time"
            },
            {
              "type": "null"
            }
          ],
          "title": "Updated At"
        },
        "expires_at": {
          "anyOf": [
            {
              "type": "string",
              "format": "date-time"
            },
            {
              "type": "null"
            }
          ],
          "title": "Expires At"
        },
        "status": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Status",
          "default": []
        },
        "nameservers": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Nameservers",
          "default": []
        },
        "dnssec": {
          "anyOf": [
            {
              "type": "boolean"
            },
            {
              "type": "null"
            }
          ],
          "title": "Dnssec"
        },
        "abuse_email": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Abuse Email"
        },
        "redacted": {
          "type": "boolean",
          "title": "Redacted",
          "default": false
        }
      },
      "type": "object",
      "title": "Registration",
      "description": "What a registry publishes about one domain. Observation only.\n\nEvery field is optional because every field is redactable: a registry that\npublishes only status codes and nameservers is the normal post-GDPR case,\nnot a failure. `status` carries the registry's own strings verbatim (RFC\n9083 spells them `client transfer prohibited`; some registries still emit\nEPP camelCase) — normalizing them would invent a vocabulary neither side\nuses."
    },
    "RequirementItem": {
      "properties": {
        "key": {
          "type": "string",
          "title": "Key"
        },
        "label": {
          "type": "string",
          "title": "Label"
        },
        "providers": {
          "additionalProperties": {
            "type": "string",
            "enum": [
              "all",
              "bulk"
            ]
          },
          "type": "object",
          "title": "Providers"
        },
        "status": {
          "type": "string",
          "enum": [
            "pass",
            "fail",
            "unknown",
            "email_test",
            "not_visible"
          ],
          "title": "Status"
        },
        "evidence": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Evidence"
        },
        "provider_status": {
          "anyOf": [
            {
              "additionalProperties": {
                "type": "string",
                "enum": [
                  "pass",
                  "fail",
                  "unknown",
                  "email_test",
                  "not_visible"
                ]
              },
              "type": "object"
            },
            {
              "type": "null"
            }
          ],
          "title": "Provider Status"
        }
      },
      "type": "object",
      "required": [
        "key",
        "label",
        "providers",
        "status"
      ],
      "title": "RequirementItem"
    },
    "SenderRequirements": {
      "properties": {
        "note": {
          "type": "string",
          "title": "Note"
        },
        "providers": {
          "items": {
            "$ref": "#/$defs/Provider"
          },
          "type": "array",
          "title": "Providers"
        },
        "items": {
          "items": {
            "$ref": "#/$defs/RequirementItem"
          },
          "type": "array",
          "title": "Items"
        }
      },
      "type": "object",
      "required": [
        "note",
        "providers",
        "items"
      ],
      "title": "SenderRequirements"
    }
  }
}

Work with this as data

Every JSON Schema 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 schemas

4 MCP tools reach this
  • find_json_schemasBrowse and filter every JSON Schema in the catalog.
  • 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 JSON Schema
curl "https://apis.io/api/v1/json-schemas/dnsdoctor-dev-scan-report-out"
All schemas
curl "https://apis.io/api/v1/json-schemas?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.