{
  "components": {
    "headers": {
      "RateLimit": {
        "description": "Where this call left the window: `\"<policy>\";r=<remaining>;t=<seconds until the window resets>`. `r` is never negative. On the `429` the plan rate limit itself answers (`details.reason: request_rate`), `r` is `0` and `t` equals `Retry-After`; a `429` for another reason keeps describing the request window, so `Retry-After` is the number to obey there.",
        "required": false,
        "schema": {
          "examples": [
            "\"app\";r=59;t=30"
          ],
          "type": "string"
        }
      },
      "RateLimitPolicy": {
        "description": "The plan rate limit that metered this call (draft-ietf-httpapi-ratelimit-headers-11): `\"<policy>\";q=<requests>;w=<window seconds>`. The policy is `app` (per app, for a secret key) or `team` (per team, for an OAuth token). Present on every response to a call authenticated with a secret key or an OAuth token, including its `429`.",
        "required": false,
        "schema": {
          "examples": [
            "\"app\";q=60;w=60"
          ],
          "type": "string"
        }
      },
      "XRateLimitLimit": {
        "description": "The plan's requests per window (the `q` of `RateLimit-Policy`).",
        "required": false,
        "schema": {
          "minimum": 1,
          "type": "integer"
        }
      },
      "XRateLimitRemaining": {
        "description": "Requests left in the current window after this one (the `r` of `RateLimit`).",
        "required": false,
        "schema": {
          "minimum": 0,
          "type": "integer"
        }
      },
      "XRateLimitReset": {
        "description": "When the current window resets, as Unix epoch seconds.",
        "required": false,
        "schema": {
          "type": "integer"
        }
      }
    },
    "parameters": {
      "DoDomainTeam": {
        "description": "The id of the team this call acts on, from `GET /api/v1/teams`. Needed only when the credential can use more than one team (an OAuth connection granted several); a secret key has exactly one. A team the credential cannot use is refused 404 `TEAM_NOT_FOUND`, the same answer whether the team exists or not.",
        "in": "header",
        "name": "DoDomain-Team",
        "required": false,
        "schema": {
          "type": "string"
        }
      }
    },
    "responses": {
      "Conflict": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "details": {},
                "docs": {
                  "type": "string"
                },
                "error": {
                  "const": "conflict",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "next": {
                  "properties": {
                    "args": {
                      "additionalProperties": {},
                      "propertyNames": {
                        "type": "string"
                      },
                      "type": "object"
                    },
                    "tool": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "tool"
                  ],
                  "type": "object"
                }
              },
              "required": [
                "error"
              ],
              "type": "object"
            }
          }
        },
        "description": "`409 conflict`: The resource is in a state that forbids the operation; for re-verify, the connection was disconnected and is no longer monitored (`details.reason` is `disconnected`)."
      },
      "Expired": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "details": {},
                "docs": {
                  "type": "string"
                },
                "error": {
                  "const": "expired",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "next": {
                  "properties": {
                    "args": {
                      "additionalProperties": {},
                      "propertyNames": {
                        "type": "string"
                      },
                      "type": "object"
                    },
                    "tool": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "tool"
                  ],
                  "type": "object"
                }
              },
              "required": [
                "error"
              ],
              "type": "object"
            }
          }
        },
        "description": "`410 expired`: The connect session's 24-hour lifetime has elapsed. Returned before any verification, metering, or webhook work happens."
      },
      "Forbidden": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "details": {},
                "docs": {
                  "type": "string"
                },
                "error": {
                  "const": "forbidden",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "next": {
                  "properties": {
                    "args": {
                      "additionalProperties": {},
                      "propertyNames": {
                        "type": "string"
                      },
                      "type": "object"
                    },
                    "tool": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "tool"
                  ],
                  "type": "object"
                }
              },
              "required": [
                "error"
              ],
              "type": "object"
            }
          }
        },
        "description": "`403 forbidden`: The OAuth access token was not granted the scope this operation requires. `details.code` is `SCOPE_MISSING` and `details.requiredScope` names the scope. Secret keys (`dd_sk_`) carry no scopes and never see this for a missing scope. The one-click start routes also answer it for a session created without one-click (a Free-plan team's session): no `details`, and the end user falls back to the manual records."
      },
      "Internal": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "details": {},
                "docs": {
                  "type": "string"
                },
                "error": {
                  "const": "internal",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "next": {
                  "properties": {
                    "args": {
                      "additionalProperties": {},
                      "propertyNames": {
                        "type": "string"
                      },
                      "type": "object"
                    },
                    "tool": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "tool"
                  ],
                  "type": "object"
                }
              },
              "required": [
                "error"
              ],
              "type": "object"
            }
          }
        },
        "description": "`500 internal`: An unhandled server-side failure. Reported to our error tracker; safe to retry."
      },
      "InvalidRequest": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "details": {},
                "docs": {
                  "type": "string"
                },
                "error": {
                  "const": "invalid_request",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "next": {
                  "properties": {
                    "args": {
                      "additionalProperties": {},
                      "propertyNames": {
                        "type": "string"
                      },
                      "type": "object"
                    },
                    "tool": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "tool"
                  ],
                  "type": "object"
                }
              },
              "required": [
                "error"
              ],
              "type": "object"
            }
          }
        },
        "description": "`400 invalid_request`: The request body or query failed schema validation, or a documented capability rule rejected it. `details` carries the field-level report when the failure came from schema validation."
      },
      "NotConfigured": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "details": {},
                "docs": {
                  "type": "string"
                },
                "error": {
                  "const": "not_configured",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "next": {
                  "properties": {
                    "args": {
                      "additionalProperties": {},
                      "propertyNames": {
                        "type": "string"
                      },
                      "type": "object"
                    },
                    "tool": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "tool"
                  ],
                  "type": "object"
                }
              },
              "required": [
                "error"
              ],
              "type": "object"
            }
          }
        },
        "description": "`503 not_configured`: The one-click flow this endpoint starts is not configured on this deployment. The manual-records flow remains available."
      },
      "NotFound": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "details": {},
                "docs": {
                  "type": "string"
                },
                "error": {
                  "const": "not_found",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "next": {
                  "properties": {
                    "args": {
                      "additionalProperties": {},
                      "propertyNames": {
                        "type": "string"
                      },
                      "type": "object"
                    },
                    "tool": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "tool"
                  ],
                  "type": "object"
                }
              },
              "required": [
                "error"
              ],
              "type": "object"
            }
          }
        },
        "description": "`404 not_found`: The addressed resource does not exist, or the caller does not own it; the two collapse to one status on purpose, so an id cannot be enumerated by a caller who does not own it."
      },
      "QuotaExceeded": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "details": {},
                "docs": {
                  "type": "string"
                },
                "error": {
                  "const": "quota_exceeded",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "next": {
                  "properties": {
                    "args": {
                      "additionalProperties": {},
                      "propertyNames": {
                        "type": "string"
                      },
                      "type": "object"
                    },
                    "tool": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "tool"
                  ],
                  "type": "object"
                }
              },
              "required": [
                "error"
              ],
              "type": "object"
            }
          }
        },
        "description": "`402 quota_exceeded`: The team's monthly verified-connection allowance is exhausted (enforced at session creation on the Free plan)."
      },
      "RateLimited": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "details": {},
                "docs": {
                  "type": "string"
                },
                "error": {
                  "const": "rate_limited",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "next": {
                  "properties": {
                    "args": {
                      "additionalProperties": {},
                      "propertyNames": {
                        "type": "string"
                      },
                      "type": "object"
                    },
                    "tool": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "tool"
                  ],
                  "type": "object"
                }
              },
              "required": [
                "error"
              ],
              "type": "object"
            }
          }
        },
        "description": "`429 rate_limited`: Rate limited. `Retry-After` (seconds) is set and `details.retryAfterSeconds` repeats it. Branch on `details.reason`: `request_rate` means the caller is calling too fast (`details.limit` carries the cap), `recently_checked` means this one connection was DNS-checked inside its cooldown.",
        "headers": {
          "RateLimit": {
            "$ref": "#/components/headers/RateLimit"
          },
          "RateLimit-Policy": {
            "$ref": "#/components/headers/RateLimitPolicy"
          },
          "Retry-After": {
            "description": "Whole seconds to wait before retrying. Always present on this response.",
            "required": true,
            "schema": {
              "minimum": 1,
              "type": "integer"
            }
          },
          "X-RateLimit-Limit": {
            "$ref": "#/components/headers/XRateLimitLimit"
          },
          "X-RateLimit-Remaining": {
            "$ref": "#/components/headers/XRateLimitRemaining"
          },
          "X-RateLimit-Reset": {
            "$ref": "#/components/headers/XRateLimitReset"
          }
        }
      },
      "Unauthorized": {
        "content": {
          "application/json": {
            "schema": {
              "properties": {
                "code": {
                  "type": "string"
                },
                "details": {},
                "docs": {
                  "type": "string"
                },
                "error": {
                  "const": "unauthorized",
                  "type": "string"
                },
                "message": {
                  "type": "string"
                },
                "next": {
                  "properties": {
                    "args": {
                      "additionalProperties": {},
                      "propertyNames": {
                        "type": "string"
                      },
                      "type": "object"
                    },
                    "tool": {
                      "type": "string"
                    }
                  },
                  "required": [
                    "tool"
                  ],
                  "type": "object"
                }
              },
              "required": [
                "error"
              ],
              "type": "object"
            }
          }
        },
        "description": "`401 unauthorized`: No `Authorization: Bearer` header, or the credential is malformed, unknown, or (for OAuth tokens) fails verification."
      }
    },
    "schemas": {
      "CheckDomainInput": {
        "properties": {
          "domain": {
            "maxLength": 253,
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "domain"
        ],
        "type": "object"
      },
      "CheckDomainResponse": {
        "properties": {
          "confidence": {
            "enum": [
              "high",
              "medium",
              "low"
            ],
            "type": "string"
          },
          "domain": {
            "type": "string"
          },
          "domainConnect": {
            "properties": {
              "discovered": {
                "type": "boolean"
              },
              "providerId": {
                "type": "string"
              },
              "providerName": {
                "type": "string"
              }
            },
            "required": [
              "discovered"
            ],
            "type": "object"
          },
          "guide": {
            "properties": {
              "apexToken": {
                "enum": [
                  "@",
                  "(blank)",
                  "%domain%"
                ],
                "type": "string"
              },
              "dashboardUrl": {
                "type": "string"
              },
              "hostFormat": {
                "type": "string"
              },
              "label": {
                "type": "string"
              },
              "notes": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "provider": {
                "type": "string"
              },
              "steps": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              }
            },
            "required": [
              "provider",
              "label",
              "hostFormat",
              "apexToken",
              "steps"
            ],
            "type": "object"
          },
          "label": {
            "type": "string"
          },
          "method": {
            "enum": [
              "oauth",
              "domain-connect",
              "guided"
            ],
            "type": "string"
          },
          "nameServers": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "oneClickAvailable": {
            "type": "boolean"
          },
          "provider": {
            "type": "string"
          },
          "tier": {
            "anyOf": [
              {
                "const": 1,
                "type": "number"
              },
              {
                "const": 2,
                "type": "number"
              },
              {
                "const": 3,
                "type": "number"
              }
            ]
          },
          "zone": {
            "type": "string"
          }
        },
        "required": [
          "domain",
          "zone",
          "provider",
          "label",
          "tier",
          "method",
          "confidence",
          "nameServers",
          "domainConnect",
          "guide",
          "oneClickAvailable"
        ],
        "type": "object"
      },
      "ConnectionSummary": {
        "properties": {
          "appId": {
            "type": "string"
          },
          "brokenAt": {
            "anyOf": [
              {
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "createdAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "disconnectedAt": {
            "anyOf": [
              {
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "domain": {
            "type": "string"
          },
          "fqdn": {
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "lastCheckedAt": {
            "anyOf": [
              {
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "recordFqdns": {
            "items": {
              "minLength": 1,
              "type": "string"
            },
            "type": "array"
          },
          "sessionId": {
            "type": "string"
          },
          "status": {
            "enum": [
              "active",
              "broken"
            ],
            "type": "string"
          },
          "verifiedAt": {
            "anyOf": [
              {
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "appId",
          "sessionId",
          "domain",
          "fqdn",
          "recordFqdns",
          "status",
          "verifiedAt",
          "lastCheckedAt",
          "brokenAt",
          "disconnectedAt",
          "createdAt"
        ],
        "type": "object"
      },
      "CreateAppInput": {
        "properties": {
          "name": {
            "maxLength": 80,
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "name"
        ],
        "type": "object"
      },
      "CreateAppResponse": {
        "properties": {
          "brandColor": {
            "type": [
              "string",
              "null"
            ]
          },
          "connectFontPreset": {
            "anyOf": [
              {
                "enum": [
                  "system",
                  "humanist",
                  "serif",
                  "rounded"
                ],
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "connectHeadline": {
            "type": [
              "string",
              "null"
            ]
          },
          "connectSubheadline": {
            "type": [
              "string",
              "null"
            ]
          },
          "connectSuccessCtaLabel": {
            "type": [
              "string",
              "null"
            ]
          },
          "connectSuccessRedirectUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "hideConnectFooterHelp": {
            "type": "boolean"
          },
          "id": {
            "type": "string"
          },
          "logoUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "name": {
            "type": "string"
          },
          "publicKey": {
            "type": "string"
          },
          "sandbox": {
            "type": "boolean"
          },
          "tlsIssuerCa": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "id",
          "name",
          "publicKey",
          "sandbox",
          "logoUrl",
          "brandColor",
          "connectHeadline",
          "connectSubheadline",
          "connectSuccessCtaLabel",
          "connectSuccessRedirectUrl",
          "connectFontPreset",
          "hideConnectFooterHelp",
          "tlsIssuerCa",
          "createdAt"
        ],
        "type": "object"
      },
      "CreateSessionInput": {
        "properties": {
          "appId": {
            "minLength": 1,
            "type": "string"
          },
          "domain": {
            "maxLength": 253,
            "pattern": "^(?!-)[a-z0-9-]{1,63}(?<!-)(\\.(?!-)[a-z0-9-]{1,63}(?<!-))+$",
            "type": "string"
          },
          "recipe": {
            "type": "string"
          },
          "records": {
            "items": {
              "properties": {
                "host": {
                  "minLength": 1,
                  "type": "string"
                },
                "priority": {
                  "maximum": 9007199254740991,
                  "minimum": -9007199254740991,
                  "type": "integer"
                },
                "ttl": {
                  "maximum": 9007199254740991,
                  "minimum": -9007199254740991,
                  "type": "integer"
                },
                "type": {
                  "enum": [
                    "A",
                    "AAAA",
                    "CNAME",
                    "TXT",
                    "MX"
                  ],
                  "type": "string"
                },
                "value": {
                  "minLength": 1,
                  "type": "string"
                }
              },
              "required": [
                "type",
                "host",
                "value"
              ],
              "type": "object"
            },
            "maxItems": 50,
            "type": "array"
          },
          "returnUrl": {
            "format": "uri",
            "maxLength": 2048,
            "type": "string"
          }
        },
        "required": [
          "domain",
          "records"
        ],
        "type": "object"
      },
      "CreateSessionResponse": {
        "properties": {
          "connectUrl": {
            "minLength": 1,
            "type": "string"
          },
          "expiresAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "records": {
            "items": {
              "properties": {
                "fqdn": {
                  "minLength": 1,
                  "type": "string"
                },
                "host": {
                  "minLength": 1,
                  "type": "string"
                },
                "type": {
                  "enum": [
                    "A",
                    "AAAA",
                    "CNAME",
                    "TXT",
                    "MX"
                  ],
                  "type": "string"
                }
              },
              "required": [
                "type",
                "host",
                "fqdn"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "token": {
            "minLength": 1,
            "type": "string"
          },
          "warnings": {
            "items": {
              "properties": {
                "code": {
                  "enum": [
                    "duplicate_host_label"
                  ],
                  "type": "string"
                },
                "fqdn": {
                  "minLength": 1,
                  "type": "string"
                },
                "host": {
                  "type": "string"
                },
                "message": {
                  "minLength": 1,
                  "type": "string"
                }
              },
              "required": [
                "code",
                "message",
                "host",
                "fqdn"
              ],
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "id",
          "token",
          "expiresAt",
          "connectUrl",
          "records"
        ],
        "type": "object"
      },
      "DeleteWebhookEndpointResponse": {
        "properties": {
          "deleted": {
            "const": true,
            "type": "boolean"
          },
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "deleted"
        ],
        "type": "object"
      },
      "DetectSessionResponse": {
        "properties": {
          "confidence": {
            "enum": [
              "high",
              "medium",
              "low"
            ],
            "type": "string"
          },
          "domainConnect": {
            "anyOf": [
              {
                "properties": {
                  "providerId": {
                    "type": "string"
                  },
                  "providerName": {
                    "type": "string"
                  }
                },
                "required": [
                  "providerId",
                  "providerName"
                ],
                "type": "object"
              },
              {
                "type": "null"
              }
            ]
          },
          "domainConnectReady": {
            "type": "boolean"
          },
          "guide": {
            "properties": {
              "apexToken": {
                "enum": [
                  "@",
                  "(blank)",
                  "%domain%"
                ],
                "type": "string"
              },
              "dashboardUrl": {
                "type": "string"
              },
              "hostFormat": {
                "type": "string"
              },
              "label": {
                "type": "string"
              },
              "notes": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              },
              "provider": {
                "type": "string"
              },
              "steps": {
                "items": {
                  "type": "string"
                },
                "type": "array"
              }
            },
            "required": [
              "provider",
              "label",
              "hostFormat",
              "apexToken",
              "steps"
            ],
            "type": "object"
          },
          "label": {
            "type": "string"
          },
          "method": {
            "enum": [
              "oauth",
              "domain-connect",
              "guided"
            ],
            "type": "string"
          },
          "nameServers": {
            "items": {
              "type": "string"
            },
            "type": "array"
          },
          "provider": {
            "type": "string"
          },
          "tier": {
            "anyOf": [
              {
                "const": 1,
                "type": "number"
              },
              {
                "const": 2,
                "type": "number"
              },
              {
                "const": 3,
                "type": "number"
              }
            ]
          },
          "zone": {
            "type": "string"
          }
        },
        "required": [
          "provider",
          "label",
          "zone",
          "tier",
          "method",
          "confidence",
          "nameServers",
          "domainConnect",
          "domainConnectReady",
          "guide"
        ],
        "type": "object"
      },
      "DisconnectConnectionResponse": {
        "properties": {
          "alreadyDisconnected": {
            "type": "boolean"
          },
          "disconnectedAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "id": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "disconnectedAt",
          "alreadyDisconnected"
        ],
        "type": "object"
      },
      "IntegratorSession": {
        "properties": {
          "appId": {
            "type": "string"
          },
          "connectionId": {
            "type": [
              "string",
              "null"
            ]
          },
          "createdAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "detectedProvider": {
            "type": [
              "string",
              "null"
            ]
          },
          "domain": {
            "type": "string"
          },
          "expired": {
            "type": "boolean"
          },
          "expiresAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "recipe": {
            "type": [
              "string",
              "null"
            ]
          },
          "records": {
            "items": {
              "properties": {
                "fqdn": {
                  "minLength": 1,
                  "type": "string"
                },
                "host": {
                  "minLength": 1,
                  "type": "string"
                },
                "type": {
                  "enum": [
                    "A",
                    "AAAA",
                    "CNAME",
                    "TXT",
                    "MX"
                  ],
                  "type": "string"
                }
              },
              "required": [
                "type",
                "host",
                "fqdn"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "status": {
            "type": "string"
          },
          "tier": {
            "anyOf": [
              {
                "maximum": 9007199254740991,
                "minimum": -9007199254740991,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          },
          "tlsIssuanceAdvisories": {
            "items": {
              "properties": {
                "code": {
                  "enum": [
                    "caa_excludes_issuer",
                    "caa_restricts_issuance",
                    "stale_acme_challenge",
                    "tls_issuance_unchecked"
                  ],
                  "type": "string"
                },
                "evidence": {
                  "items": {
                    "type": "string"
                  },
                  "type": "array"
                },
                "evidenceFqdn": {
                  "type": "string"
                },
                "fqdn": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                },
                "severity": {
                  "enum": [
                    "warning",
                    "info"
                  ],
                  "type": "string"
                }
              },
              "required": [
                "code",
                "severity",
                "fqdn",
                "evidenceFqdn",
                "evidence",
                "note"
              ],
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "id",
          "appId",
          "domain",
          "records",
          "recipe",
          "status",
          "tier",
          "detectedProvider",
          "connectionId",
          "createdAt",
          "expiresAt",
          "expired",
          "tlsIssuanceAdvisories"
        ],
        "type": "object"
      },
      "ListAppsResponse": {
        "properties": {
          "apps": {
            "items": {
              "properties": {
                "brandColor": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "connectFontPreset": {
                  "anyOf": [
                    {
                      "enum": [
                        "system",
                        "humanist",
                        "serif",
                        "rounded"
                      ],
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "connectHeadline": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "connectSubheadline": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "connectSuccessCtaLabel": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "connectSuccessRedirectUrl": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "createdAt": {
                  "format": "date-time",
                  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                  "type": "string"
                },
                "hideConnectFooterHelp": {
                  "type": "boolean"
                },
                "id": {
                  "type": "string"
                },
                "logoUrl": {
                  "type": [
                    "string",
                    "null"
                  ]
                },
                "name": {
                  "type": "string"
                },
                "publicKey": {
                  "type": "string"
                },
                "sandbox": {
                  "type": "boolean"
                },
                "tlsIssuerCa": {
                  "type": [
                    "string",
                    "null"
                  ]
                }
              },
              "required": [
                "id",
                "name",
                "publicKey",
                "sandbox",
                "logoUrl",
                "brandColor",
                "connectHeadline",
                "connectSubheadline",
                "connectSuccessCtaLabel",
                "connectSuccessRedirectUrl",
                "connectFontPreset",
                "hideConnectFooterHelp",
                "tlsIssuerCa",
                "createdAt"
              ],
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "apps"
        ],
        "type": "object"
      },
      "ListConnectionsResponse": {
        "properties": {
          "connections": {
            "items": {
              "properties": {
                "appId": {
                  "type": "string"
                },
                "brokenAt": {
                  "anyOf": [
                    {
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "createdAt": {
                  "format": "date-time",
                  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                  "type": "string"
                },
                "disconnectedAt": {
                  "anyOf": [
                    {
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "domain": {
                  "type": "string"
                },
                "fqdn": {
                  "type": "string"
                },
                "id": {
                  "type": "string"
                },
                "lastCheckedAt": {
                  "anyOf": [
                    {
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "recordFqdns": {
                  "items": {
                    "minLength": 1,
                    "type": "string"
                  },
                  "type": "array"
                },
                "sessionId": {
                  "type": "string"
                },
                "status": {
                  "enum": [
                    "active",
                    "broken"
                  ],
                  "type": "string"
                },
                "verifiedAt": {
                  "anyOf": [
                    {
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                }
              },
              "required": [
                "id",
                "appId",
                "sessionId",
                "domain",
                "fqdn",
                "recordFqdns",
                "status",
                "verifiedAt",
                "lastCheckedAt",
                "brokenAt",
                "disconnectedAt",
                "createdAt"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "nextCursor": {
            "type": [
              "string",
              "null"
            ]
          }
        },
        "required": [
          "connections",
          "nextCursor"
        ],
        "type": "object"
      },
      "ListTeamsResponse": {
        "properties": {
          "teams": {
            "items": {
              "properties": {
                "id": {
                  "type": "string"
                },
                "name": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "name"
              ],
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "teams"
        ],
        "type": "object"
      },
      "ListWebhookEndpointsResponse": {
        "properties": {
          "endpoints": {
            "items": {
              "properties": {
                "appId": {
                  "type": "string"
                },
                "createdAt": {
                  "format": "date-time",
                  "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                  "type": "string"
                },
                "id": {
                  "type": "string"
                },
                "pausedAt": {
                  "anyOf": [
                    {
                      "format": "date-time",
                      "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                      "type": "string"
                    },
                    {
                      "type": "null"
                    }
                  ]
                },
                "url": {
                  "type": "string"
                }
              },
              "required": [
                "id",
                "appId",
                "url",
                "createdAt",
                "pausedAt"
              ],
              "type": "object"
            },
            "type": "array"
          }
        },
        "required": [
          "endpoints"
        ],
        "type": "object"
      },
      "PublicSession": {
        "properties": {
          "detectedProvider": {
            "type": [
              "string",
              "null"
            ]
          },
          "domain": {
            "type": "string"
          },
          "expiresAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "recipe": {
            "type": [
              "string",
              "null"
            ]
          },
          "records": {
            "items": {
              "properties": {
                "host": {
                  "minLength": 1,
                  "type": "string"
                },
                "priority": {
                  "maximum": 9007199254740991,
                  "minimum": -9007199254740991,
                  "type": "integer"
                },
                "ttl": {
                  "maximum": 9007199254740991,
                  "minimum": -9007199254740991,
                  "type": "integer"
                },
                "type": {
                  "enum": [
                    "A",
                    "AAAA",
                    "CNAME",
                    "TXT",
                    "MX"
                  ],
                  "type": "string"
                },
                "value": {
                  "minLength": 1,
                  "type": "string"
                }
              },
              "required": [
                "type",
                "host",
                "value"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "returnUrl": {
            "type": [
              "string",
              "null"
            ]
          },
          "status": {
            "type": "string"
          },
          "tier": {
            "anyOf": [
              {
                "maximum": 9007199254740991,
                "minimum": -9007199254740991,
                "type": "integer"
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "required": [
          "id",
          "domain",
          "records",
          "recipe",
          "status",
          "tier",
          "detectedProvider",
          "returnUrl",
          "expiresAt"
        ],
        "type": "object"
      },
      "ReverifyConnectionResponse": {
        "properties": {
          "accepted": {
            "const": true,
            "type": "boolean"
          }
        },
        "required": [
          "accepted"
        ],
        "type": "object"
      },
      "RotateAppSecretKeyInput": {
        "properties": {
          "overlapHours": {
            "anyOf": [
              {
                "const": 0,
                "type": "number"
              },
              {
                "const": 1,
                "type": "number"
              },
              {
                "const": 24,
                "type": "number"
              }
            ],
            "default": 0
          }
        },
        "type": "object"
      },
      "RotateAppSecretKeyResponse": {
        "properties": {
          "appId": {
            "type": "string"
          },
          "previousKeyExpiresAt": {
            "anyOf": [
              {
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "publicKey": {
            "type": "string"
          },
          "rotatedAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "secretKey": {
            "type": "string"
          }
        },
        "required": [
          "appId",
          "publicKey",
          "secretKey",
          "rotatedAt",
          "previousKeyExpiresAt"
        ],
        "type": "object"
      },
      "SharedSetupLinkResponse": {
        "properties": {
          "expiresAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "setupUrl": {
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "setupUrl",
          "expiresAt"
        ],
        "type": "object"
      },
      "VerifySessionResponse": {
        "properties": {
          "advisories": {
            "items": {
              "properties": {
                "code": {
                  "enum": [
                    "caa_excludes_issuer",
                    "caa_restricts_issuance",
                    "stale_acme_challenge",
                    "tls_issuance_unchecked"
                  ],
                  "type": "string"
                },
                "evidence": {
                  "items": {
                    "type": "string"
                  },
                  "type": "array"
                },
                "evidenceFqdn": {
                  "type": "string"
                },
                "fqdn": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                },
                "severity": {
                  "enum": [
                    "warning",
                    "info"
                  ],
                  "type": "string"
                }
              },
              "required": [
                "code",
                "severity",
                "fqdn",
                "evidenceFqdn",
                "evidence",
                "note"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "records": {
            "items": {
              "properties": {
                "authoritativeError": {
                  "type": "string"
                },
                "authoritativeFound": {
                  "items": {
                    "type": "string"
                  },
                  "type": "array"
                },
                "fqdn": {
                  "type": "string"
                },
                "note": {
                  "type": "string"
                },
                "outcome": {
                  "enum": [
                    "verified",
                    "propagating",
                    "absent",
                    "indeterminate",
                    "domain_not_found"
                  ],
                  "type": "string"
                },
                "present": {
                  "type": "boolean"
                },
                "publicFound": {
                  "items": {
                    "type": "string"
                  },
                  "type": "array"
                },
                "type": {
                  "type": "string"
                }
              },
              "required": [
                "fqdn",
                "type",
                "present",
                "note",
                "outcome",
                "authoritativeFound",
                "publicFound"
              ],
              "type": "object"
            },
            "type": "array"
          },
          "verified": {
            "type": "boolean"
          }
        },
        "required": [
          "verified",
          "records",
          "advisories"
        ],
        "type": "object"
      },
      "WebhookEndpointInput": {
        "properties": {
          "url": {
            "minLength": 1,
            "type": "string"
          }
        },
        "required": [
          "url"
        ],
        "type": "object"
      },
      "WebhookEndpointSecretResponse": {
        "properties": {
          "appId": {
            "type": "string"
          },
          "createdAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "pausedAt": {
            "anyOf": [
              {
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "secret": {
            "type": "string"
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "appId",
          "url",
          "createdAt",
          "pausedAt",
          "secret"
        ],
        "type": "object"
      },
      "WebhookEndpointSummary": {
        "properties": {
          "appId": {
            "type": "string"
          },
          "createdAt": {
            "format": "date-time",
            "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
            "type": "string"
          },
          "id": {
            "type": "string"
          },
          "pausedAt": {
            "anyOf": [
              {
                "format": "date-time",
                "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d:[0-5]\\d(?:\\.\\d+)?(?:Z))$",
                "type": "string"
              },
              {
                "type": "null"
              }
            ]
          },
          "url": {
            "type": "string"
          }
        },
        "required": [
          "id",
          "appId",
          "url",
          "createdAt",
          "pausedAt"
        ],
        "type": "object"
      }
    },
    "securitySchemes": {
      "oauth2": {
        "description": "An OAuth 2.1 access token minted by DoDomain's own authorization server, sent as `Authorization: Bearer <jwt>`. Team-scoped rather than app-scoped, so operations that act on one app take an `appId`. Access is limited to the scopes granted at consent; a missing scope answers 403 with `details.code: SCOPE_MISSING`. Public clients register dynamically (RFC 7591) and must use PKCE.",
        "flows": {
          "authorizationCode": {
            "authorizationUrl": "https://app.dodomain.io/api/auth/oauth2/authorize",
            "refreshUrl": "https://app.dodomain.io/api/auth/oauth2/token",
            "scopes": {
              "account:delete": "Delete your DoDomain account",
              "account:read": "Read your profile, preferences, sign-in methods, referral summary and notification inbox",
              "account:write": "Change your profile, preferences and sign-in methods, export your data and manage push devices",
              "apps:delete": "Delete apps",
              "apps:read": "List the apps in your team",
              "apps:write": "Create apps and change their name, branding, origins and TLS settings",
              "billing:read": "See your plan, usage, renewal and available plans",
              "billing:write": "Start an upgrade, manage, cancel or refund your subscription",
              "connections:read": "List verified domain connections",
              "connections:write": "Disconnect connections and request re-verification of an existing connection",
              "domains:read": "Check which DNS provider manages a domain and how it can connect",
              "grants:read": "See which permissions this connection and your other connected apps hold",
              "grants:write": "Revoke connected AI apps",
              "keys:read": "List an app's API keys (never the secrets)",
              "keys:write": "Create, rotate and revoke an app's API keys",
              "sessions:read": "Read the status of domain-connect sessions",
              "sessions:write": "Create domain-connect sessions and trigger DNS verification",
              "support:read": "List the support requests you have sent",
              "support:write": "Send support requests",
              "teams:delete": "Delete teams",
              "teams:read": "See which of your teams this connection can use",
              "teams:write": "Invite, remove and change the role of teammates; create teams; switch the active team",
              "webhooks:delete": "Delete webhook endpoints",
              "webhooks:read": "List webhook endpoints and read delivery history",
              "webhooks:rotate": "Rotate webhook signing secrets",
              "webhooks:write": "Add, change, pause, resume and test webhook endpoints and redrive deliveries"
            },
            "tokenUrl": "https://app.dodomain.io/api/auth/oauth2/token"
          }
        },
        "type": "oauth2"
      },
      "secretKey": {
        "description": "An app's SECRET key, sent as `Authorization: Bearer dd_sk_...`. Server-to-server only. Never ship it to a browser. The key is app-scoped: it identifies exactly one app, carries no scope grammar, and grants that app's full access. The publishable key (`pk_*`) is not a credential and authenticates nothing.",
        "scheme": "bearer",
        "type": "http"
      }
    }
  },
  "info": {
    "description": "The `/api/v1` surface integrators call to create domain-connect sessions, drive detection and DNS verification, and manage the resulting connections.\n\n**Generated, never hand-written.** This document is produced from the same zod schemas the handlers validate with (`packages/core/src/schemas.ts`); a CI gate regenerates it on every PR and fails on any diff with the committed file, so an endpoint and its description cannot drift apart.\n\n**Errors** all share one envelope: `{ error, message?, details? }`, where `error` is one value from a finite, published code vocabulary: branch on it, never on the message prose.\n\n**Rate limits** answer `429 rate_limited` with a `Retry-After` header (whole seconds) and `details.reason` distinguishing a per-minute request cap from a per-connection re-check cooldown. Every call authenticated with a secret key or an OAuth token, success and `429` alike, also carries `RateLimit-Policy` and `RateLimit` (draft-ietf-httpapi-ratelimit-headers-11) plus `X-RateLimit-Limit`, `X-RateLimit-Remaining` and `X-RateLimit-Reset` (Unix epoch seconds).\n\n**Teams.** An OAuth connection can cover several of a person's teams. Name the one a call acts on with the `DoDomain-Team` header (an id from `GET /api/v1/teams`); with several teams and no header, a team-scoped call answers `400 invalid_request` with `details.code = \"TEAM_REQUIRED\"`, and a team the connection cannot use answers `404 not_found` with `details.code = \"TEAM_NOT_FOUND\"`; both list the usable teams in `details.teams`. A secret key has exactly one team and needs no header.",
    "summary": "Connect your customers' custom domains: DNS records written and verified.",
    "title": "DoDomain API",
    "version": "v1"
  },
  "jsonSchemaDialect": "https://json-schema.org/draft/2020-12/schema",
  "openapi": "3.1.0",
  "paths": {
    "/api/v1/apps": {
      "get": {
        "description": "A secret key sees exactly its OWN app: the key is app-scoped, and listing siblings would widen one leaked key into team-wide reconnaissance. An OAuth token with `apps:read` sees every app in the caller's team. Only the publishable key (`pk_*`) is ever returned; secret material is absent from this contract by construction.",
        "operationId": "listApps",
        "parameters": [
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListAppsResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          },
          {
            "oauth2": [
              "apps:read"
            ]
          }
        ],
        "summary": "List apps",
        "tags": [
          "Apps"
        ]
      },
      "post": {
        "description": "Requires an OAuth token with apps:write and the Admin role or higher on the acting team. App secret keys are refused with OAUTH_REQUIRED; missing scope with SCOPE_MISSING; insufficient role with TEAM_ROLE_REQUIRED. The plan app limit is enforced with quota_exceeded. Returns the same public app shape as listApps, never a secret key or hash. Pass the returned id as appId to create a connect session. Errors use RFC 9457 problem details.",
        "operationId": "createApp",
        "parameters": [
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateAppInput"
              }
            }
          },
          "description": "The app's display name.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateAppResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "details": {},
                    "docs": {
                      "type": "string"
                    },
                    "error": {
                      "const": "invalid_request",
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "next": {
                      "properties": {
                        "args": {
                          "additionalProperties": {},
                          "propertyNames": {
                            "type": "string"
                          },
                          "type": "object"
                        },
                        "tool": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "tool"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "`400 invalid_request`: The request body or query failed schema validation, or a documented capability rule rejected it. `details` carries the field-level report when the failure came from schema validation."
          },
          "401": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "details": {},
                    "docs": {
                      "type": "string"
                    },
                    "error": {
                      "const": "unauthorized",
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "next": {
                      "properties": {
                        "args": {
                          "additionalProperties": {},
                          "propertyNames": {
                            "type": "string"
                          },
                          "type": "object"
                        },
                        "tool": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "tool"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "`401 unauthorized`: No `Authorization: Bearer` header, or the credential is malformed, unknown, or (for OAuth tokens) fails verification."
          },
          "402": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "details": {},
                    "docs": {
                      "type": "string"
                    },
                    "error": {
                      "const": "quota_exceeded",
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "next": {
                      "properties": {
                        "args": {
                          "additionalProperties": {},
                          "propertyNames": {
                            "type": "string"
                          },
                          "type": "object"
                        },
                        "tool": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "tool"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "`402 quota_exceeded`: The team's monthly verified-connection allowance is exhausted (enforced at session creation on the Free plan)."
          },
          "403": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "details": {},
                    "docs": {
                      "type": "string"
                    },
                    "error": {
                      "const": "forbidden",
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "next": {
                      "properties": {
                        "args": {
                          "additionalProperties": {},
                          "propertyNames": {
                            "type": "string"
                          },
                          "type": "object"
                        },
                        "tool": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "tool"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "`403 forbidden`: The OAuth access token was not granted the scope this operation requires. `details.code` is `SCOPE_MISSING` and `details.requiredScope` names the scope. Secret keys (`dd_sk_`) carry no scopes and never see this for a missing scope. The one-click start routes also answer it for a session created without one-click (a Free-plan team's session): no `details`, and the end user falls back to the manual records."
          },
          "404": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "details": {},
                    "docs": {
                      "type": "string"
                    },
                    "error": {
                      "const": "not_found",
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "next": {
                      "properties": {
                        "args": {
                          "additionalProperties": {},
                          "propertyNames": {
                            "type": "string"
                          },
                          "type": "object"
                        },
                        "tool": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "tool"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "`404 not_found`: The addressed resource does not exist, or the caller does not own it; the two collapse to one status on purpose, so an id cannot be enumerated by a caller who does not own it."
          },
          "429": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "details": {},
                    "docs": {
                      "type": "string"
                    },
                    "error": {
                      "const": "rate_limited",
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "next": {
                      "properties": {
                        "args": {
                          "additionalProperties": {},
                          "propertyNames": {
                            "type": "string"
                          },
                          "type": "object"
                        },
                        "tool": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "tool"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "`429 rate_limited`: Rate limited. `Retry-After` (seconds) is set and `details.retryAfterSeconds` repeats it. Branch on `details.reason`: `request_rate` means the caller is calling too fast (`details.limit` carries the cap), `recently_checked` means this one connection was DNS-checked inside its cooldown.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "Retry-After": {
                "description": "Whole seconds to wait before retrying. Always present on this response.",
                "required": true,
                "schema": {
                  "minimum": 1,
                  "type": "integer"
                }
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "500": {
            "content": {
              "application/problem+json": {
                "schema": {
                  "properties": {
                    "code": {
                      "type": "string"
                    },
                    "details": {},
                    "docs": {
                      "type": "string"
                    },
                    "error": {
                      "const": "internal",
                      "type": "string"
                    },
                    "message": {
                      "type": "string"
                    },
                    "next": {
                      "properties": {
                        "args": {
                          "additionalProperties": {},
                          "propertyNames": {
                            "type": "string"
                          },
                          "type": "object"
                        },
                        "tool": {
                          "type": "string"
                        }
                      },
                      "required": [
                        "tool"
                      ],
                      "type": "object"
                    }
                  },
                  "required": [
                    "error"
                  ],
                  "type": "object"
                }
              }
            },
            "description": "`500 internal`: An unhandled server-side failure. Reported to our error tracker; safe to retry."
          }
        },
        "security": [
          {
            "oauth2": [
              "apps:write"
            ]
          }
        ],
        "summary": "Create an app",
        "tags": [
          "Apps"
        ]
      }
    },
    "/api/v1/cf/callback": {
      "get": {
        "description": "Cloudflare redirects the end user's browser here after consent. Not called by integrators. The session is found by its single-use `state` (an unknown or replayed `state` simply redirects home); the code is exchanged, the records are written through the user's own grant, verified, and the connection finalized.",
        "operationId": "handleCloudflareCallback",
        "responses": {
          "302": {
            "description": "Redirect back to the hosted connect page with a `cf` notice, or to the app root when the `state` is unknown or replayed.",
            "headers": {
              "Location": {
                "description": "Where the browser is sent next.",
                "required": true,
                "schema": {
                  "format": "uri",
                  "type": "string"
                }
              }
            }
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "Cloudflare OAuth return URL (called by Cloudflare)",
        "tags": [
          "One-click flows"
        ]
      }
    },
    "/api/v1/connections": {
      "get": {
        "description": "Newest-first list of connections and their live DNS health. A secret key lists its own app's connections; an OAuth token with `connections:read` lists team-wide and may narrow with `appId`. Disconnected connections are excluded unless `includeDisconnected=true`. Page past the 100-row ceiling by passing the previous page's `nextCursor` as `cursor`; the cursor is opaque. Never construct one.",
        "operationId": "listConnections",
        "parameters": [
          {
            "in": "query",
            "name": "appId",
            "required": false,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "domain",
            "required": false,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "limit",
            "required": false,
            "schema": {
              "default": 50,
              "maximum": 100,
              "minimum": 1,
              "type": "integer"
            }
          },
          {
            "in": "query",
            "name": "cursor",
            "required": false,
            "schema": {
              "minLength": 1,
              "type": "string"
            }
          },
          {
            "in": "query",
            "name": "includeDisconnected",
            "required": false,
            "schema": {
              "default": false,
              "type": "boolean"
            }
          },
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListConnectionsResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          },
          {
            "oauth2": [
              "connections:read"
            ]
          }
        ],
        "summary": "List connections",
        "tags": [
          "Connections"
        ]
      }
    },
    "/api/v1/connections/{connectionId}": {
      "delete": {
        "description": "Archives the connection and stops its continuous DNS monitoring, then emits one `connection.disconnected` webhook. It is not a row delete: delivery history and metering are preserved. Idempotent: a repeat returns the ORIGINAL `disconnectedAt` with `alreadyDisconnected: true` and emits nothing, so a caller's own retry loop can never double-emit. 200 rather than 204 precisely so those two fields survive. An OAuth caller must also hold the Admin role or higher on the acting team, checked live on every request; a member (or a user demoted since consent) gets `403` with `details.code = TEAM_ROLE_REQUIRED`. A secret key is app-scoped and has no role.",
        "operationId": "disconnectConnection",
        "parameters": [
          {
            "description": "The connection id from `GET /api/v1/connections`, or the `connectionId` on a `connection.*` webhook payload.",
            "in": "path",
            "name": "connectionId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DisconnectConnectionResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          },
          {
            "oauth2": [
              "connections:write"
            ]
          }
        ],
        "summary": "Disconnect a connection",
        "tags": [
          "Connections"
        ]
      },
      "get": {
        "description": "Re-reads a single connection's live DNS health by the `connectionId` every `connection.*` webhook payload carries, without listing and filtering client-side, which is impossible past the paging ceiling anyway. The body is byte-identical to one element of `GET /api/v1/connections`'s `connections[]`, so one parser serves both. Unlike the list's default, a DISCONNECTED connection is still returned: a caller naming an id already knows the row exists, and `disconnectedAt` is exactly what it came to read. The OAuth scope is `connections:read`: this reads, it never mutates.",
        "operationId": "getConnection",
        "parameters": [
          {
            "description": "The connection id from `GET /api/v1/connections`, or the `connectionId` on a `connection.*` webhook payload.",
            "in": "path",
            "name": "connectionId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ConnectionSummary"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          },
          {
            "oauth2": [
              "connections:read"
            ]
          }
        ],
        "summary": "Read one connection",
        "tags": [
          "Connections"
        ]
      }
    },
    "/api/v1/connections/{connectionId}/reverify": {
      "post": {
        "description": "Enqueues the same per-connection check the periodic monitor runs, so drift/recovery webhooks fire minutes after a support ticket instead of at the next tick. 202, not 200: the check runs in a worker and the result arrives as a `connection.verified` / `connection.failed` webhook, never in this response. `accepted: true` is deliberate rather than a job id: the check may coalesce with an already-queued job for the same connection.",
        "operationId": "reverifyConnection",
        "parameters": [
          {
            "description": "The connection id from `GET /api/v1/connections`, or the `connectionId` on a `connection.*` webhook payload.",
            "in": "path",
            "name": "connectionId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "202": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ReverifyConnectionResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          },
          {
            "oauth2": [
              "connections:write"
            ]
          }
        ],
        "summary": "Queue an on-demand DNS recheck",
        "tags": [
          "Connections"
        ]
      }
    },
    "/api/v1/digitalocean/callback": {
      "get": {
        "description": "Returns 404 while disabled. Requires state and either code or error; atomically claims unexpired state and PKCE verifier. Replays return not_found. Selects the longest-suffix owned DNS zone and checks all records before creating missing values. PUT only normalizes equivalent values, preserving TTL; conflicting destinations and mail policies are left for guided setup. No deletes. The token is never persisted and revocation is awaited on success and failure before shared DNS verification. Denial and provider errors return curated notices with manual records still available. Abandoned consent expires without disabling manual setup.",
        "operationId": "handleDigitalOceanOauthCallback",
        "responses": {
          "302": {
            "description": "Return to hosted connect with a DigitalOcean notice and embed context.",
            "headers": {
              "Location": {
                "description": "Where the browser is sent next.",
                "required": true,
                "schema": {
                  "format": "uri",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "DigitalOcean OAuth return URL",
        "tags": [
          "One-click flows"
        ]
      }
    },
    "/api/v1/domains/check": {
      "post": {
        "description": "Stateless pre-flight: which provider hosts the domain, which zone owns its records, which connect tier and flow it will get, whether one-click is actually available there (`oneClickAvailable`: `tier: 2` alone only means the provider speaks Domain Connect, and many such providers serve none of DoDomain's templates; a provider with its own one-click integration, Porkbun today, answers `true` at `tier: 3`), and the manual guide if it comes to that, without creating a session. Nothing is persisted. Available on every plan.",
        "operationId": "checkDomain",
        "parameters": [
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CheckDomainInput"
              }
            }
          },
          "description": "The domain to inspect.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CheckDomainResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          },
          {
            "oauth2": [
              "domains:read"
            ]
          }
        ],
        "summary": "Pre-flight a domain",
        "tags": [
          "Domains"
        ]
      }
    },
    "/api/v1/hostinger/callback": {
      "get": {
        "description": "Returns 404 while disabled. Requires state and either code or error; atomically claims unexpired state and PKCE verifier. Replays return not_found. Checks portfolio ownership before validating and appending DNS records with overwrite:false. The token is never persisted and revocation is awaited on success and failure before shared DNS verification. Denial and provider errors return curated notices with manual records still available. Abandoned consent expires without disabling manual setup.",
        "operationId": "handleHostingerOauthCallback",
        "responses": {
          "302": {
            "description": "Return to hosted connect with a Hostinger notice and embed context.",
            "headers": {
              "Location": {
                "description": "Where the browser is sent next.",
                "required": true,
                "schema": {
                  "format": "uri",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "Hostinger OAuth return URL",
        "tags": [
          "One-click flows"
        ]
      }
    },
    "/api/v1/keys/rotate": {
      "post": {
        "description": "The calling app's secret key rotates ITSELF, and the new `dd_sk_...` is returned **once**: this is what makes scheduled credential rotation automatable instead of a dashboard click.\n\n**SELF-rotation only, deliberately.** There is no create, list, or revoke-another-key on this API: key inventory stays behind a human dashboard session. A stolen `dd_sk_` can already do everything the app can do until it is rotated, but it must not be able to mint a second, hidden credential that survives the owner rotating the one they know about. Rotating is, in effect, the revoke.\n\n**DEFAULT: no grace window.** With no body (or `overlapHours: 0`) the previous key stops authenticating the instant this returns, so **this response is the only copy of the new key**. A caller that drops it has locked itself out of the API and must rotate again from the dashboard. A zero-overlap rotate also **terminates any overlap window still live** from an earlier rotation; rotating with the default is how you revoke a previous key early.\n\n**OPT-IN OVERLAP.** `{\"overlapHours\": 1}` or `{\"overlapHours\": 24}` keeps the old key authenticating alongside the new one until `previousKeyExpiresAt`, so a rotator can deploy the new key with zero downtime. **Exactly one previous key is ever kept:** rotating again overwrites the slot and key n-1 dies instantly, regardless of remaining window. `publicKey` is echoed unchanged (it identifies the app in the widget and is never rotated here) so a CI job can assert it rewrote the right app.",
        "operationId": "rotateAppSecretKey",
        "parameters": [
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/RotateAppSecretKeyInput"
              }
            }
          },
          "description": "Optional. Absent (the pre-window contract) or `overlapHours: 0` = immediate cutover; 1 | 24 = the overlap window in hours.",
          "required": false
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/RotateAppSecretKeyResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          }
        ],
        "summary": "Rotate the calling app's secret key",
        "tags": [
          "Credentials"
        ]
      }
    },
    "/api/v1/porkbun/callback": {
      "get": {
        "description": "Porkbun returns state and status (applied, partial, denied or error); a denial carries access_denied, and an error carries a code (domain_not_in_account, domain_not_active or invalid_records; any other code gets a generic notice). Returns 404 while disabled. Validates the entire callback and atomically consumes its matching, unexpired session state. For applied and partial, reads every requested record back through authoritative DNS before entering the shared verification engine; provider status alone never finalizes a connection, and a partial apply finalizes only when every requested record resolves. Denied consent and errors redirect with curated user-facing notices.",
        "operationId": "handlePorkbunDnsConnectCallback",
        "responses": {
          "302": {
            "description": "Return to the hosted connect page with a Porkbun result notice and embed context.",
            "headers": {
              "Location": {
                "description": "Where the browser is sent next.",
                "required": true,
                "schema": {
                  "format": "uri",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "409": {
            "$ref": "#/components/responses/Conflict"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "Porkbun DNS Connect return URL",
        "tags": [
          "One-click flows"
        ]
      }
    },
    "/api/v1/sessions": {
      "post": {
        "description": "Mints a short-lived (24h) session for one domain and the DNS records the end user must create, and returns the hosted `connectUrl` plus the fully-qualified names verification will look up. Hosts are composed relative to the session's `domain`. Read `records[].fqdn` in the response to confirm what will actually be checked. An OAuth caller is team-scoped and must send `appId`; a secret-key caller's app is implicit in the key and a mismatching `appId` is rejected. An OAuth caller must also hold the Admin role or higher on the acting team, checked live on every request; a member (or a user demoted since consent) gets `403` with `details.code = TEAM_ROLE_REQUIRED`. One session carries at most 50 records; more is `400 invalid_request` with `details.reason = too_many_records` and `details.limit` (the cap).",
        "operationId": "createSession",
        "parameters": [
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/CreateSessionInput"
              }
            }
          },
          "description": "The domain plus the records to connect.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/CreateSessionResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          },
          {
            "oauth2": [
              "sessions:write"
            ]
          }
        ],
        "summary": "Create a connect session",
        "tags": [
          "Sessions"
        ]
      }
    },
    "/api/v1/sessions/{token}": {
      "get": {
        "description": "ONE path with TWO arms, discriminated by the SHAPE of the path segment, never by which credential you send.\n\n**Token arm**: pass the `dd_sess_...` token and send no `Authorization` header. Public: the token is the capability. Returns `PublicSession`: the records exactly as you requested them, plus status, detected provider and expiry. Once the session's 24 hours are up this arm answers `410 expired` forever, which is correct for a capability URL and useless for observability, hence the other arm.\n\n**Id arm**: pass the session id and authenticate with a `dd_sk_` secret key or an OAuth token carrying `sessions:read`. Returns `IntegratorSession`, and it can do two things the token arm cannot: it is addressable by the `sessionId` that every webhook payload carries (the token never appears in one), and **it reads an EXPIRED session** rather than refusing it, reporting `expired: true` instead of `410`. That flag is DERIVED at read (`expiresAt <= now`), so it is already true in the window before the reaper persists `status: \"expired\"`; do not infer expiry from `status` alone. Its `records` are the COMPOSED names (`type`/`host`/`fqdn`) actually looked up on DNS, not the raw request echo the token arm returns.\n\n`tier` and `detectedProvider` are `null` on both arms until detection runs: that pair is the 'detection has not run yet' signal, never a detection failure.",
        "operationId": "getSession",
        "parameters": [
          {
            "description": "Either a session token (`dd_sess_...`) or a session id (the `sessionId` every webhook payload carries). The handler discriminates on the value's shape, not on your credential: a token makes this the PUBLIC arm (the token itself is the capability, so treat it like a bearer token), an id makes it the integrator-authed arm.",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "oneOf": [
                    {
                      "$ref": "#/components/schemas/PublicSession"
                    },
                    {
                      "$ref": "#/components/schemas/IntegratorSession"
                    }
                  ]
                }
              }
            },
            "description": "`PublicSession` when addressed by token, `IntegratorSession` when addressed by id. The two are told apart by their fields: only `IntegratorSession` carries `appId`, `connectionId`, `createdAt` and `expired`.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {},
          {
            "secretKey": []
          },
          {
            "oauth2": [
              "sessions:read"
            ]
          }
        ],
        "summary": "Read a connect session (by token, or by id with a credential)",
        "tags": [
          "Sessions"
        ]
      }
    },
    "/api/v1/sessions/{token}/cloudflare/start": {
      "get": {
        "description": "Browser entry point for the tier-1 Cloudflare flow: mints a PKCE pair and a CSRF `state`, binds them to the session, and redirects the end user to Cloudflare's consent page. Navigate to it (a link or a redirect). Do not fetch it from script. One-click is a paid-plan feature: a session created by a Free team was created without it, and this route answers `403 forbidden` for it (the hosted page never shows that session a one-click button).",
        "operationId": "startCloudflareAuthorization",
        "parameters": [
          {
            "description": "The session token returned by `POST /api/v1/sessions` (`dd_sess_...`). This value IS the credential for the operation. Treat it like a bearer token and never log or share it.",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to Cloudflare's OAuth consent page.",
            "headers": {
              "Location": {
                "description": "Where the browser is sent next.",
                "required": true,
                "schema": {
                  "format": "uri",
                  "type": "string"
                }
              }
            }
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/NotConfigured"
          }
        },
        "security": [],
        "summary": "Start the Cloudflare one-click flow",
        "tags": [
          "One-click flows"
        ]
      }
    },
    "/api/v1/sessions/{token}/detect": {
      "post": {
        "description": "Runs provider detection for the session's domain and persists the resulting tier on the session. `zone` is the zone that OWNS the records (the registrable apex, or a delegated subzone when one exists); that is the zone the end user opens at their provider, which is why the returned `guide` is written against it rather than against the subdomain being connected.",
        "operationId": "detectSessionProvider",
        "parameters": [
          {
            "description": "The session token returned by `POST /api/v1/sessions` (`dd_sess_...`). This value IS the credential for the operation. Treat it like a bearer token and never log or share it.",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DetectSessionResponse"
                }
              }
            },
            "description": "Success."
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "Detect the session domain's DNS provider",
        "tags": [
          "Sessions"
        ]
      }
    },
    "/api/v1/sessions/{token}/digitalocean/start": {
      "get": {
        "description": "Browser entry point. Returns 404 without side effects unless DIGITALOCEAN_OAUTH_CLIENT_ID is configured. Paid-plan sessions only. Uses public-client OAuth with PKCE S256 and bounded single-use server state. Requests only domain:read, domain:create and domain:update; no delete permission. DCR token audience and scope acceptance remain unverified until a live rehearsal. Unsupported records redirect to guided setup. Navigate to this URL rather than fetching it.",
        "operationId": "startDigitalOceanOauth",
        "parameters": [
          {
            "description": "The session token returned by `POST /api/v1/sessions` (`dd_sess_...`). This value IS the credential for the operation. Treat it like a bearer token and never log or share it.",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to DigitalOcean consent or guided setup.",
            "headers": {
              "Location": {
                "description": "Where the browser is sent next.",
                "required": true,
                "schema": {
                  "format": "uri",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "Start DigitalOcean one-click setup",
        "tags": [
          "One-click flows"
        ]
      }
    },
    "/api/v1/sessions/{token}/domain-connect/callback": {
      "get": {
        "description": "The DNS provider redirects the end user's browser here after the apply page. Not called by integrators. The single-use `state` minted at start is re-derived and mismatches are refused before anything is written; the session's records are then re-verified against authoritative DNS, and the connection is finalized only when every record is live.",
        "operationId": "handleDomainConnectCallback",
        "parameters": [
          {
            "description": "The session token returned by `POST /api/v1/sessions` (`dd_sess_...`). This value IS the credential for the operation. Treat it like a bearer token and never log or share it.",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect back to the hosted connect page, carrying a `dc` notice describing the outcome.",
            "headers": {
              "Location": {
                "description": "Where the browser is sent next.",
                "required": true,
                "schema": {
                  "format": "uri",
                  "type": "string"
                }
              }
            }
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "Domain Connect return URL (called by the DNS provider)",
        "tags": [
          "One-click flows"
        ]
      }
    },
    "/api/v1/sessions/{token}/domain-connect/start": {
      "get": {
        "description": "Browser entry point for the tier-2 Domain Connect flow: compiles the session's records into a published template, discovers the owning zone's DNS provider, confirms the provider has onboarded that template, and redirects the end user to the provider's own consent/apply page. A redirect back is never treated as proof of success; the callback re-verifies against authoritative DNS. Navigate to it. Do not fetch it from script. One-click is a paid-plan feature: a session created by a Free team was created without it, and this route answers `403 forbidden` for it (the hosted page never shows that session a one-click button).",
        "operationId": "startDomainConnectApply",
        "parameters": [
          {
            "description": "The session token returned by `POST /api/v1/sessions` (`dd_sess_...`). This value IS the credential for the operation. Treat it like a bearer token and never log or share it.",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to the DNS provider's Domain Connect apply page, or, when the provider has not onboarded the template (or discovery fails), back to the hosted connect page carrying a `dc` notice so the end user can fall back to the manual records.",
            "headers": {
              "Location": {
                "description": "Where the browser is sent next.",
                "required": true,
                "schema": {
                  "format": "uri",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          },
          "503": {
            "$ref": "#/components/responses/NotConfigured"
          }
        },
        "security": [],
        "summary": "Start the Domain Connect one-click flow",
        "tags": [
          "One-click flows"
        ]
      }
    },
    "/api/v1/sessions/{token}/hostinger/start": {
      "get": {
        "description": "Browser entry point. Returns 404 without side effects unless HOSTINGER_OAUTH_CLIENT_ID is configured. Paid-plan sessions only. Uses public-client OAuth with PKCE S256 and bounded single-use server state. Hostinger grants account-wide access with no scopes. Unsupported records redirect to guided setup. Navigate to this URL rather than fetching it.",
        "operationId": "startHostingerOauth",
        "parameters": [
          {
            "description": "The session token returned by `POST /api/v1/sessions` (`dd_sess_...`). This value IS the credential for the operation. Treat it like a bearer token and never log or share it.",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to Hostinger consent or guided setup.",
            "headers": {
              "Location": {
                "description": "Where the browser is sent next.",
                "required": true,
                "schema": {
                  "format": "uri",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "Start Hostinger one-click setup",
        "tags": [
          "One-click flows"
        ]
      }
    },
    "/api/v1/sessions/{token}/porkbun/start": {
      "get": {
        "description": "Browser entry point. Available only when the server has a Porkbun DNS Connect client secret; otherwise returns 404 without session changes. Paid-plan sessions only. Signs an HS256 JWT containing the domain, up to 25 requested records, the registered callback, a single-use state and a bounded expiry. Porkbun presents and applies the requested changes during consent; no OAuth token or standing API access is granted. Navigate to this URL rather than fetching it.",
        "operationId": "startPorkbunDnsConnect",
        "parameters": [
          {
            "description": "The session token returned by `POST /api/v1/sessions` (`dd_sess_...`). This value IS the credential for the operation. Treat it like a bearer token and never log or share it.",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "302": {
            "description": "Redirect to Porkbun's consent page.",
            "headers": {
              "Location": {
                "description": "Where the browser is sent next.",
                "required": true,
                "schema": {
                  "format": "uri",
                  "type": "string"
                }
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "Start the Porkbun DNS Connect flow",
        "tags": [
          "One-click flows"
        ]
      }
    },
    "/api/v1/sessions/{token}/shared-setup-link": {
      "post": {
        "description": "The person who starts a connect flow is often not the person who can edit DNS. This mints a SECOND, narrower link for that person: `setupUrl` opens a page showing the integrator's branding, the domain, the exact records to add and a **Verify** button, and nothing else. No one-click flow, no `returnUrl` back into your product, and never the `dd_sess_` session token itself, so it is safe to forward to a stranger in a way the connect link is not.\n\nAuthorized by the session token, exactly like `verify`, so the hosted page calls it from the browser and your server can call it with the token it minted. The link lives exactly as long as the session (`expiresAt` is the session's) and answers `410 expired` with it. **Calling this again ROTATES the link**: the previous `setupUrl` stops resolving the moment the new one is returned, so there is never more than one live setup link per session.\n\nA verify pressed from the setup page finalizes the connection and fires `connection.verified` / `session.completed` exactly as a verify from the hosted page does; the only trace of which door was used is on the session's attempt log in the dashboard.",
        "operationId": "createSharedSetupLink",
        "parameters": [
          {
            "description": "The session token returned by `POST /api/v1/sessions` (`dd_sess_...`). This value IS the credential for the operation. Treat it like a bearer token and never log or share it.",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/SharedSetupLinkResponse"
                }
              }
            },
            "description": "Success."
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "Create a setup link to forward to the domain's DNS admin",
        "tags": [
          "Sessions"
        ]
      }
    },
    "/api/v1/sessions/{token}/verify": {
      "post": {
        "description": "Checks every expected record against authoritative nameservers. When all are present the session is marked verified, the connection is recorded, usage is metered and webhooks fire, all in one transaction, and only on an actual authoritative match. `records[].outcome` distinguishes a genuine absence (`absent`), a still-propagating record (`propagating`), a check that could not be completed (`indeterminate`) and a domain that does not resolve at all (`domain_not_found`).",
        "operationId": "verifySession",
        "parameters": [
          {
            "description": "The session token returned by `POST /api/v1/sessions` (`dd_sess_...`). This value IS the credential for the operation. Treat it like a bearer token and never log or share it.",
            "in": "path",
            "name": "token",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/VerifySessionResponse"
                }
              }
            },
            "description": "Success."
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "410": {
            "$ref": "#/components/responses/Expired"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [],
        "summary": "Verify the session's records against live DNS",
        "tags": [
          "Sessions"
        ]
      }
    },
    "/api/v1/teams": {
      "get": {
        "description": "The teams the caller may act on, with the ids the `DoDomain-Team` header takes. An OAuth connection lists the teams its grant covers that the person still belongs to: one, several, or every team they are on, as chosen on the consent screen. A secret key lists its own app's one team. Only ids and names are returned.",
        "operationId": "listTeams",
        "parameters": [
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListTeamsResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          },
          {
            "oauth2": [
              "teams:read"
            ]
          }
        ],
        "summary": "List the teams this credential can use",
        "tags": [
          "Teams"
        ]
      }
    },
    "/api/v1/webhook-endpoints": {
      "get": {
        "description": "The app's delivery targets. NEVER returns `secret`: the signing secret is show-once at create and rotate, so a leaked read cannot recover the ability to forge our signatures. The app is implicit in the key, and that is also the authorization: a key only ever sees its OWN app's endpoints.",
        "operationId": "listWebhookEndpoints",
        "parameters": [
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/ListWebhookEndpointsResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          }
        ],
        "summary": "List webhook endpoints",
        "tags": [
          "Webhook endpoints"
        ]
      },
      "post": {
        "description": "Registers one delivery target and returns its signing secret **once**. `secret` (`whsec_...`) appears in this response and in a rotate response, and in no read surface ever again. Store it now. The URL must be https and must not resolve to a localhost/private/link-local address, and an app cannot register the same normalized URL twice: both refusals are `400 invalid_request` with the reason in `message`. A hostname that resolves only to private, reserved, CGNAT or loopback addresses is refused the same way, with `details.reason = webhook_url_resolves_private` for clients that branch on it (a host that does not resolve yet is accepted). At the plan's endpoint cap it is `402 quota_exceeded`.",
        "operationId": "createWebhookEndpoint",
        "parameters": [
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointInput"
              }
            }
          },
          "description": "The https URL to deliver to.",
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointSecretResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "402": {
            "$ref": "#/components/responses/QuotaExceeded"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          }
        ],
        "summary": "Register a webhook endpoint",
        "tags": [
          "Webhook endpoints"
        ]
      }
    },
    "/api/v1/webhook-endpoints/{endpointId}": {
      "delete": {
        "description": "Stops delivering to the endpoint. Its past delivery rows survive as evidence, but a failed delivery to a deleted endpoint can no longer be redriven, the same irreversible outcome the dashboard's confirm dialog spells out. 200 with a body rather than 204, matching `DELETE /api/v1/connections/{connectionId}`, so an automated caller can log WHAT it removed.",
        "operationId": "deleteWebhookEndpoint",
        "parameters": [
          {
            "description": "The endpoint id from `GET /api/v1/webhook-endpoints`.",
            "in": "path",
            "name": "endpointId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/DeleteWebhookEndpointResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          }
        ],
        "summary": "Delete a webhook endpoint",
        "tags": [
          "Webhook endpoints"
        ]
      },
      "patch": {
        "description": "Points an existing endpoint at a new URL. PATCH rather than PUT because `url` is the only mutable field, so the body is a partial by construction. The signing secret is deliberately UNTOUCHED: moving hosts must not force a receiver to re-key; rotating is the explicit sibling call. Same URL policy and same duplicate rule as create; the plan cap is NOT re-checked, because an update never grows the set and an app already over a lowered cap must still be able to fix a wrong URL. A URL that actually CHANGES also resumes an auto-paused endpoint (`pausedAt` back to null) and restarts its 7-day clock. A new address is the usual fix for a dead one. An unknown id and another app's id both answer 404.",
        "operationId": "updateWebhookEndpoint",
        "parameters": [
          {
            "description": "The endpoint id from `GET /api/v1/webhook-endpoints`.",
            "in": "path",
            "name": "endpointId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/WebhookEndpointInput"
              }
            }
          },
          "description": "The new https URL.",
          "required": true
        },
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointSummary"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "400": {
            "$ref": "#/components/responses/InvalidRequest"
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          }
        ],
        "summary": "Repoint a webhook endpoint",
        "tags": [
          "Webhook endpoints"
        ]
      }
    },
    "/api/v1/webhook-endpoints/{endpointId}/resume": {
      "post": {
        "description": "DoDomain pauses an endpoint automatically once it has had NO successful delivery for 7 days AND at least 5 dead-lettered deliveries in that span (measured from the later of its last successful delivery and its creation, or its last resume). While paused (`pausedAt` set), new events are still recorded as deliveries but marked skipped and never sent, and redriving any of its deliveries is refused. This call resumes it: `pausedAt` back to null and the 7-day clock restarted. A verb sub-path, like rotate-secret, because resuming is an action rather than a property you set. Idempotent: an endpoint that is not paused comes back unchanged. It does NOT resend the skipped deliveries: redrive them from the dashboard, each with its original delivery id. A PATCH that changes the URL resumes the endpoint too. An unknown id and another app's id both answer 404.",
        "operationId": "resumeWebhookEndpoint",
        "parameters": [
          {
            "description": "The endpoint id from `GET /api/v1/webhook-endpoints`.",
            "in": "path",
            "name": "endpointId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointSummary"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          }
        ],
        "summary": "Resume an auto-paused webhook endpoint",
        "tags": [
          "Webhook endpoints"
        ]
      }
    },
    "/api/v1/webhook-endpoints/{endpointId}/rotate-secret": {
      "post": {
        "description": "Mints a new signing secret and returns it **once**. A verb sub-path rather than a field on PATCH (mirroring `POST /api/v1/connections/{connectionId}/reverify`) because rotation is an ACTION with a once-only result, not a property you can idempotently PUT; that also keeps the show-once secret out of every ordinary update response. **IMMEDIATE CUTOVER, no dual-secret window:** the worker reads the secret live at delivery time, so signatures switch at once, including retries of deliveries created before the rotation. Deploy the new secret to your receiver promptly.",
        "operationId": "rotateWebhookEndpointSecret",
        "parameters": [
          {
            "description": "The endpoint id from `GET /api/v1/webhook-endpoints`.",
            "in": "path",
            "name": "endpointId",
            "required": true,
            "schema": {
              "type": "string"
            }
          },
          {
            "$ref": "#/components/parameters/DoDomainTeam"
          }
        ],
        "responses": {
          "200": {
            "content": {
              "application/json": {
                "schema": {
                  "$ref": "#/components/schemas/WebhookEndpointSecretResponse"
                }
              }
            },
            "description": "Success.",
            "headers": {
              "RateLimit": {
                "$ref": "#/components/headers/RateLimit"
              },
              "RateLimit-Policy": {
                "$ref": "#/components/headers/RateLimitPolicy"
              },
              "X-RateLimit-Limit": {
                "$ref": "#/components/headers/XRateLimitLimit"
              },
              "X-RateLimit-Remaining": {
                "$ref": "#/components/headers/XRateLimitRemaining"
              },
              "X-RateLimit-Reset": {
                "$ref": "#/components/headers/XRateLimitReset"
              }
            }
          },
          "401": {
            "$ref": "#/components/responses/Unauthorized"
          },
          "403": {
            "$ref": "#/components/responses/Forbidden"
          },
          "404": {
            "$ref": "#/components/responses/NotFound"
          },
          "429": {
            "$ref": "#/components/responses/RateLimited"
          },
          "500": {
            "$ref": "#/components/responses/Internal"
          }
        },
        "security": [
          {
            "secretKey": []
          }
        ],
        "summary": "Rotate a webhook endpoint's signing secret",
        "tags": [
          "Webhook endpoints"
        ]
      }
    }
  },
  "servers": [
    {
      "description": "Production",
      "url": "https://app.dodomain.io"
    }
  ],
  "tags": [
    {
      "description": "Create a connect session, read its state, and drive detection and DNS verification for it.",
      "name": "Sessions"
    },
    {
      "description": "Browser-navigated entry points and provider return URLs for the Cloudflare (tier 1) and Domain Connect (tier 2) one-click paths.",
      "name": "One-click flows"
    },
    {
      "description": "Stateless domain pre-flight: no session required.",
      "name": "Domains"
    },
    {
      "description": "The apps in your team and their publishable keys.",
      "name": "Apps"
    },
    {
      "description": "The teams a credential may act on, and the ids the `DoDomain-Team` header takes.",
      "name": "Teams"
    },
    {
      "description": "Verified connections, their live DNS health, re-verification and disconnection.",
      "name": "Connections"
    },
    {
      "description": "Where DoDomain delivers your app's events, and the signing secrets those deliveries are signed with.",
      "name": "Webhook endpoints"
    },
    {
      "description": "Rotation of an app's own secret key. These operations accept the secret key ONLY. An OAuth access token is refused.",
      "name": "Credentials"
    }
  ]
}
