---
id: w7/m6/t004
title: Render parity — collision + reserved rejection shape/semantics vs Render's "domain already in use"
worker: worker7
status: todo
estimate: 30m
depends_on: [w7/m6/t003]
---

## Objective

Confirm the collision + reserved-host rejection this milestone adds is consistent across REST, GraphQL, and MCP (same status/error shape/message) and matches Render's behavior — Render blocks a duplicate domain with "This domain already exists on another site." Flag any drift as follow-up rather than diverging silently.

## Context

- bex-api is meant to be Render-compatible (`docs/ADR006-bex-api.md`); update the custom-domain row in `docs/ADR018-render-parity.md` to record the duplicate-domain block now that it exists.
- Render performs **no** standalone ownership challenge — do not add one; parity here is specifically the duplicate/"in use" block (Render) plus bex's reserved-host guard (a bex-specific superset, note it as such).
- Surfaces to cross-check: `rest.go`, the GraphQL add-domain resolver, the MCP add-domain tool, and the dashboard form.

## Steps

1. Diff the rejection status + error shape across REST/GraphQL/MCP; assert consistency.
2. Compare the duplicate-domain semantics/message against Render's; note the reserved-host guard as a deliberate bex superset (Render has no `*.onbex.co`-equivalent platform-host concept).
3. Update the `docs/ADR018-render-parity.md` custom-domain row with the duplicate-domain-block evidence.

## Files

- `docs/ADR018-render-parity.md` (ledger row update)
- read-only cross-check of the three adapters + dashboard

## Acceptance criteria

- [ ] Rejection status + error shape are consistent across REST/GraphQL/MCP; any divergence documented or filed.
- [ ] Duplicate-domain behavior compared against Render; reserved-host guard recorded as an intentional bex superset; parity ledger updated.

## Out of scope

- Behavior changes (t001–t003); Simplify (t005); tests (t006).
