---
title: "Release App Domain"
method: DELETE
path: "/apps/{app_id}/domains/{hostname}"
tags: ["App Domains"]
---

# Release App Domain

`DELETE /apps/{app_id}/domains/{hostname}`

## Request

- Base URL: `https://be.graph8.com/api/v1`
- URL: `https://be.graph8.com/api/v1/apps/{app_id}/domains/{hostname}`
- Auth: HTTP bearer

Release a claimed hostname.

Hostnames are globally unique across every app
(``uq_developer_app_domains_hostname``), so one typo permanently burned that
host for every builder on the platform with no public way to undo it. The
repository had get, list, claim and set_status, and no delete at all.

A HARD delete, not a `revoked` status. The uniqueness index does not care
about status, so a row left behind in any state keeps the host claimed while
looking to the builder like it was released.

OWNERSHIP IS CHECKED AGAINST THE STORED CLAIM, not just the app in the path.
`_require_own_app` proves the caller owns THIS app; the second check proves
the hostname belongs to that same app. Without it a builder could pass their
own app id and any hostname, and delete a competitor's claim -- the one thing
global uniqueness makes irreversible.

404 for an unknown host or one held by another app, never 403: a 403 would
make a competitor's claim discoverable by probing.

Idempotent within your own app: releasing a host you have already released is
a 404, because after the first call the claim genuinely does not exist. The
delete itself reports whether a row went, and both answers are correct.

## Path parameters

- `app_id` string, required
- `hostname` string, required

## Headers

- `Authorization` string, nullable
- `X-Org-Id` string, nullable

## Response `200`

Successful Response

- ApiResponseDict
  - `data` object, required
  - `pagination` PaginationMeta
    - `page` integer, required: Current page number (1-indexed)
    - `limit` integer, required: Items per page
    - `total` integer, required: Total number of items
    - `has_next` boolean, required: Whether there are more pages
    - `next_cursor` string, nullable: Opaque cursor for the next page; null when there are no more items. Pass it back as the `cursor` query param to fetch the following page without computing page numbers.
    - `total_available` integer, nullable: Search endpoints only: how many records really match, when known. Can be larger than `total`, which is capped at the 10,000 results that can be paged. For company search this is a distinct-company estimate. Null when unknown; absent outside search.
    - `total_is_lower_bound` boolean, nullable: Search endpoints only: true when more records match than `total` shows (the result set is larger than the 10,000 results that can be paged). Absent outside search.
    - `notes` string[], nullable: Search endpoints only: plain-language notes about the result, e.g. that more records match than can be paged, or that a reversed `between` range was put in order.

## Other responses

- `422`: Validation Error

---

[API](https://skmtc.dev/graph8/apis/graph8-developer-api.md) · [All operations](https://skmtc.dev/graph8/apis/graph8-developer-api/llms.txt) · [OpenAPI document](https://skmtc.dev/graph8/apis/graph8-developer-api/revisions/2e7cfa0ca921?raw)
