{
  "openapi": "3.1.0",
  "info": {
    "title": "Nymwell Control Plane API",
    "version": "0.6.0",
    "description": "Control-plane API for organizations, RBAC, hashed API keys, approval-bound DNS, billing, audit evidence and MCP, plus the domain lifecycle: search and suggestions, quotes for registration, renewal and transfer, orders paid on a hosted checkout, registrar calls with idempotency keys, approval-bound domain settings, nameservers, contacts, DNSSEC and forwarding, grace-period cancellation, and registrar webhooks. Execution runs against whatever DOMAIN_PROVIDER and BILLING_PROVIDER configure (mock, Name.com sandbox, Stripe test mode); live money needs explicit flags. GET /api/v1/status says which."
  },
  "paths": {
    "/api/v1/status": {
      "get": {
        "summary": "Read Worker, database and provider-adapter readiness",
        "responses": {
          "200": {
            "description": "Control-plane readiness; provider execution capabilities remain explicit"
          }
        }
      }
    },
    "/api/v1/domains/search": {
      "get": {
        "summary": "Check registrar availability and USD prices for a domain, or a name across endings",
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 253
            }
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 63
            },
            "description": "A name without an ending"
          },
          {
            "name": "tlds",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated endings tried with q"
          }
        ],
        "responses": {
          "200": {
            "description": "Availability from the registrar itself, with retail prices for the minimum term"
          },
          "400": {
            "description": "Invalid domain"
          },
          "503": {
            "description": "Registrar adapter not configured; no availability was invented"
          },
          "429": {
            "description": "Rate limited; see Retry-After"
          }
        }
      }
    },
    "/api/v1/session": {
      "get": {
        "summary": "Return the signed-in Sites identity and default organization",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      }
    },
    "/api/v1/dashboard": {
      "get": {
        "summary": "Return organization portfolio, DNS, billing, approval and support metrics",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      }
    },
    "/api/v1/domains": {
      "get": {
        "summary": "List domains for the authenticated organization",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      }
    },
    "/api/v1/dns/records": {
      "get": {
        "summary": "List persisted records for an authorized managed zone",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        },
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 253
            }
          },
          {
            "name": "refresh",
            "in": "query",
            "schema": {
              "enum": [
                "1"
              ]
            },
            "description": "Re-read the records from the registrar first"
          }
        ]
      }
    },
    "/api/v1/billing/invoices": {
      "get": {
        "summary": "List organization invoice metadata",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      }
    },
    "/api/v1/organizations/members": {
      "get": {
        "summary": "List organization members and roles",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      }
    },
    "/api/v1/organizations/invitations": {
      "get": {
        "summary": "List pending and historical workspace invitations",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      },
      "post": {
        "summary": "Record a scoped workspace invitation; delivery depends on the email adapter",
        "security": [
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "email",
                  "role"
                ],
                "properties": {
                  "email": {
                    "type": "string",
                    "format": "email"
                  },
                  "role": {
                    "enum": [
                      "admin",
                      "billing_admin",
                      "domain_manager",
                      "dns_operator",
                      "viewer"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Invitation recorded"
          },
          "403": {
            "description": "Owner or admin role required"
          }
        }
      }
    },
    "/api/v1/organizations/policies": {
      "get": {
        "summary": "List scoped authorization policies",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      }
    },
    "/api/v1/api-keys": {
      "get": {
        "summary": "List API-key metadata; raw secrets are never returned",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      },
      "post": {
        "summary": "Issue a scoped, expiring API key; the raw secret is returned once",
        "security": [
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/ApiKeyRequest"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Key created; one-time secret included"
          },
          "400": {
            "description": "Invalid key policy"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Owner or admin role required"
          }
        }
      }
    },
    "/api/v1/approvals": {
      "get": {
        "summary": "List approval requests for the authenticated organization",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      },
      "post": {
        "summary": "Record a human decision for an exact prepared action",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Provider-specific request contract"
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Origin, scope or policy denied"
          },
          "503": {
            "description": "Required provider adapter is not configured; performed is false"
          }
        }
      }
    },
    "/api/v1/support/tickets": {
      "get": {
        "summary": "List workspace support tickets",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        }
      },
      "post": {
        "summary": "Create a workspace support ticket",
        "security": [
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "subject",
                  "message",
                  "category"
                ],
                "properties": {
                  "subject": {
                    "type": "string"
                  },
                  "message": {
                    "type": "string"
                  },
                  "category": {
                    "enum": [
                      "domain",
                      "dns",
                      "billing",
                      "transfer",
                      "account",
                      "other"
                    ]
                  },
                  "priority": {
                    "enum": [
                      "low",
                      "normal",
                      "high",
                      "urgent"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Ticket created"
          }
        }
      }
    },
    "/api/v1/audit-events": {
      "get": {
        "summary": "List immutable organization audit evidence",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Authorized organization data"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or role denied"
          },
          "503": {
            "description": "Database unavailable"
          }
        },
        "parameters": [
          {
            "name": "limit",
            "in": "query",
            "schema": {
              "type": "integer",
              "minimum": 1,
              "maximum": 100,
              "default": 50
            }
          }
        ]
      }
    },
    "/api/v1/dns/change-sets": {
      "post": {
        "summary": "Prepare DNS creates, updates and deletes; applied at the registrar in order once approved",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/DnsChangeSet"
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Change set waiting for approval"
          },
          "400": {
            "description": "Invalid change set"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Scope or domain allowlist denied"
          },
          "404": {
            "description": "Managed zone not found"
          }
        }
      }
    },
    "/api/v1/domains/import": {
      "post": {
        "summary": "Track an externally registered domain without changing its registrar or nameservers",
        "security": [
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "expires_at": {
                    "type": "string",
                    "format": "date"
                  },
                  "label": {
                    "type": "string",
                    "maxLength": 120
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Domain and pending external DNS zone created"
          },
          "409": {
            "description": "Domain already tracked"
          }
        }
      }
    },
    "/api/v1/quotes": {
      "post": {
        "summary": "Create an expiring provider-backed domain quote",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/QuoteRequest"
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Origin, scope or policy denied"
          },
          "503": {
            "description": "Required provider adapter is not configured; performed is false"
          },
          "201": {
            "description": "Quote locked at the registrar price for 40 minutes (USD)"
          }
        }
      }
    },
    "/api/v1/orders": {
      "post": {
        "summary": "Create an order for a quote and return the hosted checkout a person pays on",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "quote_id"
                ],
                "properties": {
                  "quote_id": {
                    "type": "string"
                  },
                  "dns": {
                    "type": "object",
                    "properties": {
                      "preset": {
                        "type": "string"
                      },
                      "target": {
                        "type": "string"
                      },
                      "records": {
                        "type": "array",
                        "maxItems": 20
                      }
                    }
                  },
                  "idempotency_key": {
                    "type": "string",
                    "maxLength": 120
                  },
                  "accept_trademark_notice": {
                    "type": "boolean",
                    "description": "Required when the quote carried a trademark notice"
                  },
                  "tld_requirements": {
                    "type": "object",
                    "additionalProperties": {
                      "type": "string"
                    }
                  },
                  "locale": {
                    "enum": [
                      "en",
                      "zh-tw"
                    ],
                    "description": "Language of the console page the person returns to after paying"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Origin, scope or policy denied"
          },
          "503": {
            "description": "Required provider adapter is not configured; performed is false"
          },
          "201": {
            "description": "Order awaiting payment, with checkout_url; paying is the approval"
          }
        }
      },
      "get": {
        "summary": "Read an order: awaiting_payment, registering, registered, needs_attention, failed_refunded",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "parameters": [
          {
            "name": "id",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Order view"
          },
          "404": {
            "description": "Order not found in this organization"
          }
        }
      }
    },
    "/api/v1/renewals": {
      "post": {
        "summary": "Renewal quote for a domain held through Nymwell; pay it with POST /api/v1/orders",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "years": {
                    "type": "integer",
                    "minimum": 1,
                    "maximum": 10
                  }
                }
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Origin, scope or policy denied"
          },
          "503": {
            "description": "Required provider adapter is not configured; performed is false"
          },
          "201": {
            "description": "Renewal quote locked for 40 minutes (USD)"
          }
        }
      }
    },
    "/api/v1/transfers": {
      "post": {
        "summary": "The signed-in owner submits the auth code for a transfer in; it is sealed and the transfer quote is returned. Agent keys are refused.",
        "security": [
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain",
                  "auth_code"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "auth_code": {
                    "type": "string",
                    "maxLength": 128,
                    "writeOnly": true
                  }
                }
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Origin, scope or policy denied"
          },
          "503": {
            "description": "Required provider adapter is not configured; performed is false"
          },
          "201": {
            "description": "Transfer request and quote; pay the quote with POST /api/v1/orders"
          }
        }
      },
      "get": {
        "summary": "Transfers into Nymwell and their status",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Transfer requests"
          }
        }
      }
    },
    "/api/v1/billing/payment-intents": {
      "post": {
        "summary": "Create payment for an exact quote",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Provider-specific request contract"
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Origin, scope or policy denied"
          },
          "503": {
            "description": "Required provider adapter is not configured; performed is false"
          }
        }
      }
    },
    "/api/v1/agent-actions": {
      "post": {
        "summary": "Prepare a policy-evaluated provider action",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "description": "Provider-specific request contract"
              }
            }
          }
        },
        "responses": {
          "400": {
            "description": "Invalid input"
          },
          "401": {
            "description": "Authentication required"
          },
          "403": {
            "description": "Origin, scope or policy denied"
          },
          "503": {
            "description": "Required provider adapter is not configured; performed is false"
          }
        }
      }
    },
    "/api/v1/domains/screen": {
      "get": {
        "summary": "Screen names against public registry records (RDAP) and DNS: registered, listed for sale, in use or no record. Not availability.",
        "parameters": [
          {
            "name": "names",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated domains, up to 20"
          },
          {
            "name": "q",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string",
              "maxLength": 63
            },
            "description": "A name without an ending"
          },
          {
            "name": "tlds",
            "in": "query",
            "required": false,
            "schema": {
              "type": "string"
            },
            "description": "Endings tried with q; default com,ai,dev,app,io,co,tw"
          }
        ],
        "responses": {
          "200": {
            "description": "Per-name status: registered, registered_for_sale, in_use, no_record or unknown"
          },
          "400": {
            "description": "Invalid domain"
          }
        }
      }
    },
    "/api/v1/dns/presets": {
      "get": {
        "summary": "List DNS presets for common hosts",
        "responses": {
          "200": {
            "description": "Presets and the records each creates"
          }
        }
      }
    },
    "/api/v1/webhooks/stripe": {
      "post": {
        "summary": "Stripe webhook (checkout.session.completed / expired); Stripe-Signature verified, livemode checked, replay-safe",
        "responses": {
          "200": {
            "description": "Received"
          },
          "400": {
            "description": "Invalid signature or livemode mismatch"
          },
          "503": {
            "description": "Stripe not configured"
          }
        }
      }
    },
    "/internal/cron": {
      "post": {
        "summary": "Run reconciliation: expire quotes, sweep payments, retry uncertain orders, poll transfers, sync domains, mark expiries, send renewal reminders; exists only with CRON_SECRET",
        "security": [
          {
            "cronSecret": []
          }
        ],
        "responses": {
          "200": {
            "description": "Reconciliation summary"
          },
          "401": {
            "description": "Wrong secret"
          }
        }
      }
    },
    "/api/v1/domains/suggest": {
      "get": {
        "summary": "Registrar suggestions for a keyword, registration inventory only (public, rate limited)",
        "parameters": [
          {
            "name": "q",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 63
            }
          },
          {
            "name": "tlds",
            "in": "query",
            "schema": {
              "type": "string"
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Purchasable names with retail prices"
          },
          "429": {
            "description": "Rate limited; see Retry-After"
          },
          "503": {
            "description": "Registrar not configured"
          }
        }
      }
    },
    "/api/v1/domains/tld-info": {
      "get": {
        "summary": "Prices and rules for endings: allowed terms, grace days after expiry, privacy, DNSSEC, HSTS (public)",
        "parameters": [
          {
            "name": "tlds",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string"
            },
            "description": "Comma-separated, up to 10"
          }
        ],
        "responses": {
          "200": {
            "description": "Per-ending prices and rules"
          },
          "503": {
            "description": "Registrar not configured"
          }
        }
      }
    },
    "/api/v1/domains/detail": {
      "get": {
        "summary": "Live registrar state of a domain: expiry, locks, privacy, nameservers, contact verification, transfer",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 253
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Domain detail; contacts only for signed-in people"
          },
          "409": {
            "description": "Not held through Nymwell"
          }
        }
      }
    },
    "/api/v1/domains/auto-renew": {
      "post": {
        "summary": "Record the renewal preference (no money moves; the registrar auto-renew stays off)",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain",
                  "enabled"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "enabled": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Preference recorded"
          }
        }
      }
    },
    "/api/v1/domains/settings": {
      "post": {
        "summary": "Prepare a WHOIS privacy or transfer lock change",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "privacy_enabled": {
                    "type": "boolean"
                  },
                  "transfer_lock": {
                    "type": "boolean"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Approval requested; nothing changes at the registrar until an owner or admin approves (POST /api/v1/approvals)"
          },
          "400": {
            "description": "Invalid input"
          },
          "403": {
            "description": "Role, scope or domain allowlist denied"
          },
          "404": {
            "description": "Domain not found in this organization"
          },
          "409": {
            "description": "Domain is not held through Nymwell"
          },
          "503": {
            "description": "Registrar not configured; performed is false"
          }
        }
      }
    },
    "/api/v1/domains/nameservers": {
      "post": {
        "summary": "Prepare a nameserver change (2-13 hosts, or \"default\" for Nymwell DNS)",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain",
                  "nameservers"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "nameservers": {
                    "oneOf": [
                      {
                        "type": "array",
                        "items": {
                          "type": "string"
                        },
                        "minItems": 2,
                        "maxItems": 13
                      },
                      {
                        "const": "default"
                      }
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Approval requested; nothing changes at the registrar until an owner or admin approves (POST /api/v1/approvals)"
          },
          "400": {
            "description": "Invalid input"
          },
          "403": {
            "description": "Role, scope or domain allowlist denied"
          },
          "404": {
            "description": "Domain not found in this organization"
          },
          "409": {
            "description": "Domain is not held through Nymwell"
          },
          "503": {
            "description": "Registrar not configured; performed is false"
          }
        }
      }
    },
    "/api/v1/domains/contacts": {
      "post": {
        "summary": "Prepare a contact change; a new registrant must confirm by email within 15 days",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain",
                  "contacts"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "contacts": {
                    "type": "object",
                    "required": [
                      "registrant"
                    ],
                    "properties": {
                      "registrant": {
                        "type": "object",
                        "required": [
                          "firstName",
                          "lastName",
                          "address1",
                          "city",
                          "country",
                          "email",
                          "phone"
                        ],
                        "properties": {
                          "firstName": {
                            "type": "string"
                          },
                          "lastName": {
                            "type": "string"
                          },
                          "companyName": {
                            "type": "string"
                          },
                          "address1": {
                            "type": "string"
                          },
                          "address2": {
                            "type": "string"
                          },
                          "city": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string"
                          },
                          "zip": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          },
                          "phone": {
                            "type": "string"
                          },
                          "fax": {
                            "type": "string"
                          }
                        }
                      },
                      "admin": {
                        "type": "object",
                        "required": [
                          "firstName",
                          "lastName",
                          "address1",
                          "city",
                          "country",
                          "email",
                          "phone"
                        ],
                        "properties": {
                          "firstName": {
                            "type": "string"
                          },
                          "lastName": {
                            "type": "string"
                          },
                          "companyName": {
                            "type": "string"
                          },
                          "address1": {
                            "type": "string"
                          },
                          "address2": {
                            "type": "string"
                          },
                          "city": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string"
                          },
                          "zip": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          },
                          "phone": {
                            "type": "string"
                          },
                          "fax": {
                            "type": "string"
                          }
                        }
                      },
                      "tech": {
                        "type": "object",
                        "required": [
                          "firstName",
                          "lastName",
                          "address1",
                          "city",
                          "country",
                          "email",
                          "phone"
                        ],
                        "properties": {
                          "firstName": {
                            "type": "string"
                          },
                          "lastName": {
                            "type": "string"
                          },
                          "companyName": {
                            "type": "string"
                          },
                          "address1": {
                            "type": "string"
                          },
                          "address2": {
                            "type": "string"
                          },
                          "city": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string"
                          },
                          "zip": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          },
                          "phone": {
                            "type": "string"
                          },
                          "fax": {
                            "type": "string"
                          }
                        }
                      },
                      "billing": {
                        "type": "object",
                        "required": [
                          "firstName",
                          "lastName",
                          "address1",
                          "city",
                          "country",
                          "email",
                          "phone"
                        ],
                        "properties": {
                          "firstName": {
                            "type": "string"
                          },
                          "lastName": {
                            "type": "string"
                          },
                          "companyName": {
                            "type": "string"
                          },
                          "address1": {
                            "type": "string"
                          },
                          "address2": {
                            "type": "string"
                          },
                          "city": {
                            "type": "string"
                          },
                          "state": {
                            "type": "string"
                          },
                          "zip": {
                            "type": "string"
                          },
                          "country": {
                            "type": "string"
                          },
                          "email": {
                            "type": "string"
                          },
                          "phone": {
                            "type": "string"
                          },
                          "fax": {
                            "type": "string"
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Approval requested; nothing changes at the registrar until an owner or admin approves (POST /api/v1/approvals)"
          },
          "400": {
            "description": "Invalid input"
          },
          "403": {
            "description": "Role, scope or domain allowlist denied"
          },
          "404": {
            "description": "Domain not found in this organization"
          },
          "409": {
            "description": "Domain is not held through Nymwell"
          },
          "503": {
            "description": "Registrar not configured; performed is false"
          }
        }
      }
    },
    "/api/v1/domains/auth-code": {
      "post": {
        "summary": "Prepare unlocking and revealing the transfer auth code; shown once to the approver",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Approval requested; nothing changes at the registrar until an owner or admin approves (POST /api/v1/approvals)"
          },
          "400": {
            "description": "Invalid input"
          },
          "403": {
            "description": "Role, scope or domain allowlist denied"
          },
          "404": {
            "description": "Domain not found in this organization"
          },
          "409": {
            "description": "Domain is not held through Nymwell"
          },
          "503": {
            "description": "Registrar not configured; performed is false"
          }
        }
      }
    },
    "/api/v1/domains/cancel": {
      "post": {
        "summary": "Prepare cancelling a registration inside the add grace period (deleted and refunded)",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Approval requested; nothing changes at the registrar until an owner or admin approves (POST /api/v1/approvals)"
          },
          "400": {
            "description": "Invalid input"
          },
          "403": {
            "description": "Role, scope or domain allowlist denied"
          },
          "404": {
            "description": "Domain not found in this organization"
          },
          "409": {
            "description": "Domain is not held through Nymwell"
          },
          "503": {
            "description": "Registrar not configured; performed is false"
          }
        }
      }
    },
    "/api/v1/domains/services": {
      "get": {
        "summary": "Live DNSSEC, URL forwarding, email forwarding or vanity nameservers",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "parameters": [
          {
            "name": "domain",
            "in": "query",
            "required": true,
            "schema": {
              "type": "string",
              "maxLength": 253
            }
          },
          {
            "name": "service",
            "in": "query",
            "required": true,
            "schema": {
              "enum": [
                "dnssec",
                "url_forwarding",
                "email_forwarding",
                "vanity_nameservers"
              ]
            }
          }
        ],
        "responses": {
          "200": {
            "description": "Items from the registrar"
          }
        }
      },
      "post": {
        "summary": "Prepare a change to one of those services",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain",
                  "service",
                  "operation",
                  "data"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "service": {
                    "enum": [
                      "dnssec",
                      "url_forwarding",
                      "email_forwarding",
                      "vanity_nameservers"
                    ]
                  },
                  "operation": {
                    "enum": [
                      "create",
                      "update",
                      "delete"
                    ]
                  },
                  "data": {
                    "type": "object"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Approval requested; nothing changes at the registrar until an owner or admin approves (POST /api/v1/approvals)"
          },
          "400": {
            "description": "Invalid input"
          },
          "403": {
            "description": "Role, scope or domain allowlist denied"
          },
          "404": {
            "description": "Domain not found in this organization"
          },
          "409": {
            "description": "Domain is not held through Nymwell"
          },
          "503": {
            "description": "Registrar not configured; performed is false"
          }
        }
      }
    },
    "/api/v1/transfers/cancel": {
      "post": {
        "summary": "Prepare cancelling a transfer in (refunded) or out",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "domain",
                  "direction"
                ],
                "properties": {
                  "domain": {
                    "type": "string"
                  },
                  "direction": {
                    "enum": [
                      "in",
                      "out"
                    ]
                  }
                }
              }
            }
          }
        },
        "responses": {
          "201": {
            "description": "Approval requested; nothing changes at the registrar until an owner or admin approves (POST /api/v1/approvals)"
          },
          "400": {
            "description": "Invalid input"
          },
          "403": {
            "description": "Role, scope or domain allowlist denied"
          },
          "404": {
            "description": "Domain not found in this organization"
          },
          "409": {
            "description": "Domain is not held through Nymwell"
          },
          "503": {
            "description": "Registrar not configured; performed is false"
          }
        }
      }
    },
    "/api/v1/contacts/unverified": {
      "get": {
        "summary": "Registrant emails awaiting ICANN verification on this organization's domains",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Verification ids, deadlines and domains"
          }
        }
      }
    },
    "/api/v1/contacts/resend": {
      "post": {
        "summary": "Resend an ICANN verification email",
        "security": [
          {
            "apiKey": []
          },
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "verification_id"
                ],
                "properties": {
                  "verification_id": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Sent, with the next eligible time"
          },
          "404": {
            "description": "Not one of this organization's verifications"
          }
        }
      }
    },
    "/api/v1/webhooks/namecom": {
      "post": {
        "summary": "Name.com events (expiry, removal, locks, transfers, registry rejection and compliance, contact verification, balance); X-NAMECOM-SIGNATURE verified, processed once",
        "responses": {
          "200": {
            "description": "Received"
          },
          "400": {
            "description": "Invalid signature or event"
          },
          "503": {
            "description": "Registrar not configured"
          }
        }
      }
    },
    "/api/v1/admin/registrar": {
      "get": {
        "summary": "Internal: registrar connection, balance, webhook subscriptions, recent events, order reconciliation",
        "security": [
          {
            "sitesIdentity": []
          }
        ],
        "responses": {
          "200": {
            "description": "Registrar operations view"
          },
          "403": {
            "description": "Internal admins only"
          }
        }
      },
      "post": {
        "summary": "Internal: registrar operations (subscribe_webhooks, sync_domains, tld_prices, premium_list, verify_contact, create_sub_account, internal_transfer_in, ...)",
        "security": [
          {
            "sitesIdentity": []
          }
        ],
        "requestBody": {
          "required": true,
          "content": {
            "application/json": {
              "schema": {
                "type": "object",
                "required": [
                  "operation"
                ],
                "properties": {
                  "operation": {
                    "type": "string"
                  }
                }
              }
            }
          }
        },
        "responses": {
          "200": {
            "description": "Operation result"
          },
          "403": {
            "description": "Internal admins only, or not approved by the registrar"
          }
        }
      }
    }
  },
  "components": {
    "securitySchemes": {
      "apiKey": {
        "type": "apiKey",
        "in": "header",
        "name": "X-Nymwell-Key",
        "description": "nw_live_... or nw_test_...; scoped, expiring and stored only as a hash."
      },
      "sitesIdentity": {
        "type": "apiKey",
        "in": "header",
        "name": "oai-authenticated-user-id",
        "description": "Injected by Sites after Sign in with ChatGPT; clients must not synthesize this header. Honoured only while IDENTITY_PROVIDER is sites."
      },
      "cronSecret": {
        "type": "http",
        "scheme": "bearer",
        "description": "CRON_SECRET"
      }
    },
    "schemas": {
      "QuoteRequest": {
        "type": "object",
        "required": [
          "domain",
          "action",
          "years",
          "currency"
        ],
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "action": {
            "enum": [
              "register",
              "renew",
              "transfer"
            ]
          },
          "years": {
            "type": "integer",
            "minimum": 1,
            "maximum": 10
          },
          "currency": {
            "enum": [
              "USD",
              "TWD"
            ]
          }
        }
      },
      "ApiKeyRequest": {
        "type": "object",
        "required": [
          "name",
          "scopes",
          "expires_in_days"
        ],
        "additionalProperties": false,
        "properties": {
          "name": {
            "type": "string",
            "minLength": 1,
            "maxLength": 80
          },
          "scopes": {
            "type": "array",
            "minItems": 1,
            "uniqueItems": true,
            "items": {
              "enum": [
                "domains:read",
                "dns:read",
                "quotes:create",
                "orders:create",
                "actions:prepare",
                "actions:read"
              ]
            }
          },
          "domain_allowlist": {
            "type": "array",
            "maxItems": 100,
            "items": {
              "type": "string"
            }
          },
          "expires_in_days": {
            "type": "integer",
            "minimum": 1,
            "maximum": 90
          },
          "environment": {
            "enum": [
              "test",
              "live"
            ]
          }
        }
      },
      "DnsChangeSet": {
        "type": "object",
        "required": [
          "domain",
          "changes"
        ],
        "additionalProperties": false,
        "properties": {
          "domain": {
            "type": "string"
          },
          "changes": {
            "type": "array",
            "minItems": 1,
            "maxItems": 100,
            "items": {
              "type": "object",
              "required": [
                "operation",
                "type",
                "name",
                "value"
              ],
              "additionalProperties": false,
              "properties": {
                "operation": {
                  "enum": [
                    "create",
                    "update",
                    "delete"
                  ]
                },
                "type": {
                  "enum": [
                    "A",
                    "AAAA",
                    "CNAME",
                    "MX",
                    "TXT",
                    "CAA",
                    "SRV",
                    "NS"
                  ]
                },
                "name": {
                  "type": "string"
                },
                "value": {
                  "type": "string"
                },
                "ttl": {
                  "type": "integer",
                  "minimum": 1
                },
                "priority": {
                  "type": "integer",
                  "minimum": 0,
                  "maximum": 65535
                }
              }
            }
          }
        }
      }
    }
  }
}