DNS Doctor · Schema

ReadinessOut

The serialized `ReadinessVerdict` — `next_record` only when ready. D125/D130: `record_withheld` marks a ready `p=reject` step whose record the plan does not include; `withheld_reason` (`"plan"`) and `pricing_url` (a `/pricing` page, never a checkout) say why and where, null otherwise. D167: on the agent read (`GET /v1/readiness`, `get_readiness`) a hosted domain's ready step is withheld too, `withheld_reason` `"hosted"`: we serve its record, so there is nothing for the owner to publish (relay `hosted.note`).

DNSDNS ToolsEmail AuthenticationWeb Application

Properties

Name Type Description
ready boolean
current_step object
next_step object
next_record object
blockers array
window_days integer
total_messages integer
rejected_unknown object
progress object
enrollment object
evidence object
record_withheld boolean
withheld_reason object
pricing_url object
aligned_pct object
hosted object
View JSON Schema on GitHub

JSON Schema

dnsdoctor-dev-readiness-out-schema.json Raw ↑
{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/dnsdoctor-dev/json-schema/dnsdoctor-dev-readiness-out-schema.json",
  "title": "ReadinessOut",
  "description": "The serialized `ReadinessVerdict` — `next_record` only when ready.\n\nD125/D130: `record_withheld` marks a ready `p=reject` step whose record the\nplan does not include; `withheld_reason` (`\"plan\"`) and `pricing_url` (a\n`/pricing` page, never a checkout) say why and where, null otherwise. D167: on\nthe agent read (`GET /v1/readiness`, `get_readiness`) a hosted domain's ready\nstep is withheld too, `withheld_reason` `\"hosted\"`: we serve its record, so\nthere is nothing for the owner to publish (relay `hosted.note`).",
  "x-generated": "2026-11-05",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/dnsdoctor-dev-openapi.json#/components/schemas/ReadinessOut",
  "properties": {
    "ready": {
      "type": "boolean",
      "title": "Ready"
    },
    "current_step": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Current Step"
    },
    "next_step": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Next Step"
    },
    "next_record": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Next Record"
    },
    "blockers": {
      "items": {
        "$ref": "#/$defs/BlockerOut"
      },
      "type": "array",
      "title": "Blockers"
    },
    "window_days": {
      "type": "integer",
      "title": "Window Days"
    },
    "total_messages": {
      "type": "integer",
      "title": "Total Messages"
    },
    "rejected_unknown": {
      "anyOf": [
        {
          "$ref": "#/$defs/RejectedUnknownOut"
        },
        {
          "type": "null"
        }
      ]
    },
    "progress": {
      "$ref": "#/$defs/ProgressOut"
    },
    "enrollment": {
      "$ref": "#/$defs/EnrollmentStatusOut"
    },
    "evidence": {
      "$ref": "#/$defs/dns_doctor__rua__readiness_out__EvidenceOut"
    },
    "record_withheld": {
      "type": "boolean",
      "title": "Record Withheld",
      "default": false
    },
    "withheld_reason": {
      "anyOf": [
        {
          "type": "string",
          "enum": [
            "plan",
            "hosted"
          ]
        },
        {
          "type": "null"
        }
      ],
      "title": "Withheld Reason"
    },
    "pricing_url": {
      "anyOf": [
        {
          "type": "string"
        },
        {
          "type": "null"
        }
      ],
      "title": "Pricing Url"
    },
    "aligned_pct": {
      "anyOf": [
        {
          "type": "number"
        },
        {
          "type": "null"
        }
      ],
      "title": "Aligned Pct"
    },
    "hosted": {
      "anyOf": [
        {
          "$ref": "#/$defs/HostedReadinessOut"
        },
        {
          "type": "null"
        }
      ]
    }
  },
  "type": "object",
  "required": [
    "ready",
    "current_step",
    "next_step",
    "next_record",
    "blockers",
    "window_days",
    "total_messages",
    "progress",
    "enrollment",
    "evidence"
  ],
  "$defs": {
    "BlockerOut": {
      "properties": {
        "source": {
          "type": "string",
          "title": "Source"
        },
        "metric": {
          "type": "string",
          "title": "Metric"
        },
        "fix_hint": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Fix Hint"
        },
        "ip": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Ip"
        },
        "senders": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Senders"
        },
        "ips": {
          "anyOf": [
            {
              "items": {
                "type": "string"
              },
              "type": "array"
            },
            {
              "type": "null"
            }
          ],
          "title": "Ips"
        }
      },
      "type": "object",
      "required": [
        "source",
        "metric",
        "fix_hint"
      ],
      "title": "BlockerOut",
      "description": "One reason the next policy step stays locked."
    },
    "EnrollmentState": {
      "type": "string",
      "enum": [
        "not_published",
        "published_waiting",
        "published_silent",
        "unknown",
        "receiving"
      ],
      "title": "EnrollmentState",
      "description": "Whether a monitored domain's owner has actually published our rua= address.\n\n`total_messages == 0` alone conflates three very different situations; this\nenum separates them so the dashboard can stop promising reports that can\nnever arrive (design §3.2):\n\n- `NOT_PUBLISHED`: our address is absent from the live rua= list.\n- `PUBLISHED_WAITING`: present, no reports yet, < `silent_hours` since first seen.\n- `PUBLISHED_SILENT`: present, no reports yet, ≥ `silent_hours` since first seen.\n- `RECEIVING`: reports arrived in the window (decided by volume, so it can\n  never contradict the readiness verdict derived from the same read).\n- `UNKNOWN` (D165): the DMARC record could not be read just now and no rescan\n  ever saw our address — neither \"published\" nor \"not published\"."
    },
    "EnrollmentStatusOut": {
      "properties": {
        "state": {
          "$ref": "#/$defs/EnrollmentState"
        },
        "published_at": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Published At"
        },
        "checked_at": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Checked At"
        }
      },
      "type": "object",
      "required": [
        "state",
        "published_at",
        "checked_at"
      ],
      "title": "EnrollmentStatusOut",
      "description": "The four-state RUA enrollment verdict (design §3.4).\n\nNamed `EnrollmentStatusOut` because `EnrollmentOut` is already the enrollment\nroute's model (`rua/enroll.py`). Carries **no record field by construction**,\nso an LLM-shaped record string can never reach this surface (the invariant)."
    },
    "HostedReadinessOut": {
      "properties": {
        "note": {
          "type": "string",
          "title": "Note"
        },
        "next_step_at": {
          "anyOf": [
            {
              "type": "string"
            },
            {
              "type": "null"
            }
          ],
          "title": "Next Step At"
        }
      },
      "type": "object",
      "required": [
        "note"
      ],
      "title": "HostedReadinessOut",
      "description": "D167 M3/M6: DNS Doctor hosts this row's DMARC record, so the next step is\napplied from the dashboard, never published by hand. `next_step_at` is when a\nscheduled automatic step runs (ISO), null when none is scheduled."
    },
    "ProgressOut": {
      "properties": {
        "progress_pct": {
          "type": "number",
          "title": "Progress Pct"
        },
        "step_base_pct": {
          "type": "integer",
          "title": "Step Base Pct"
        },
        "evidence_fraction": {
          "type": "number",
          "title": "Evidence Fraction"
        },
        "days_with_data": {
          "type": "integer",
          "title": "Days With Data"
        },
        "worst_source_aligned_pct": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Worst Source Aligned Pct"
        },
        "unknown_volume_pct": {
          "type": "number",
          "title": "Unknown Volume Pct"
        },
        "stage": {
          "type": "string",
          "title": "Stage"
        },
        "eta_days": {
          "anyOf": [
            {
              "type": "integer"
            },
            {
              "type": "null"
            }
          ],
          "title": "Eta Days"
        }
      },
      "type": "object",
      "required": [
        "progress_pct",
        "step_base_pct",
        "evidence_fraction",
        "days_with_data",
        "worst_source_aligned_pct",
        "unknown_volume_pct",
        "stage"
      ],
      "title": "ProgressOut",
      "description": "The serialized D48 progress bar — the dashboard renders, never recomputes."
    },
    "RejectedUnknownOut": {
      "properties": {
        "sources": {
          "type": "integer",
          "title": "Sources"
        },
        "messages": {
          "type": "integer",
          "title": "Messages"
        }
      },
      "type": "object",
      "required": [
        "sources",
        "messages"
      ],
      "title": "RejectedUnknownOut",
      "description": "D113 — unaligned unknown mail the published `p=reject` already rejects.\n\nPresent only at the terminal rung, where the unknown-sources blocker is not\nemitted: the same D50 aggregate, read as the policy's receipt rather than as\na reason to withhold a step that does not exist."
    },
    "dns_doctor__rua__readiness_out__EvidenceOut": {
      "properties": {
        "trusted_reports": {
          "type": "integer",
          "title": "Trusted Reports"
        },
        "untrusted_reports": {
          "type": "integer",
          "title": "Untrusted Reports"
        },
        "reporters": {
          "items": {
            "type": "string"
          },
          "type": "array",
          "title": "Reporters"
        },
        "dominant_report_share": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ],
          "title": "Dominant Report Share"
        },
        "days_with_trusted_data": {
          "type": "integer",
          "title": "Days With Trusted Data"
        },
        "concentrated": {
          "type": "boolean",
          "title": "Concentrated"
        }
      },
      "type": "object",
      "required": [
        "trusted_reports",
        "untrusted_reports",
        "reporters",
        "dominant_report_share",
        "days_with_trusted_data",
        "concentrated"
      ],
      "title": "EvidenceOut",
      "description": "What the verdict was computed from (D111) — counts and names, no prose.\n\nAdditive: every readiness surface (dashboard, `GET /v1/readiness`, the\n`get_readiness` MCP tool) serializes through `serialize_readiness`, so this\nobject appears on all three at once and can never differ between them.\n`concentrated` is a flag, not a gate — one reporter supplying nearly all the\nevidence is worth saying, and nothing is withheld for it."
    }
  }
}

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-readiness-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.